I don't think I'm going to catch up in time. :(
This commit is contained in:
1
data/day16.txt
Normal file
1
data/day16.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
C20D718021600ACDC372CD8DE7A057252A49C940239D68978F7970194EA7CCB310088760088803304A0AC1B100721EC298D3307440041CD8B8005D12DFD27CBEEF27D94A4E9B033006A45FE71D665ACC0259C689B1F99679F717003225900465800804E39CE38CE161007E52F1AEF5EE6EC33600BCC29CFFA3D8291006A92CA7E00B4A8F497E16A675EFB6B0058F2D0BD7AE1371DA34E730F66009443C00A566BFDBE643135FEDF321D000C6269EA66545899739ADEAF0EB6C3A200B6F40179DE31CB7B277392FA1C0A95F6E3983A100993801B800021B0722243D00042E0DC7383D332443004E463295176801F29EDDAA853DBB5508802859F2E9D2A9308924F9F31700AA4F39F720C733A669EC7356AC7D8E85C95E123799D4C44C0109C0AF00427E3CC678873F1E633C4020085E60D340109E3196023006040188C910A3A80021B1763FC620004321B4138E52D75A20096E4718D3E50016B19E0BA802325E858762D1802B28AD401A9880310E61041400043E2AC7E8A4800434DB24A384A4019401C92C154B43595B830002BC497ED9CC27CE686A6A43925B8A9CFFE3A9616E5793447004A4BBB749841500B26C5E6E306899C5B4C70924B77EF254B48688041CD004A726ED3FAECBDB2295AEBD984E08E0065C101812E006380126005A80124048CB010D4C03DC900E16A007200B98E00580091EE004B006902004B00410000AF00015933223100688010985116A311803D05E3CC4B300660BC7283C00081CF26491049F3D690E9802739661E00D400010A8B91F2118803310A2F43396699D533005E37E8023311A4BB9961524A4E2C027EC8C6F5952C2528B333FA4AD386C0A56F39C7DB77200C92801019E799E7B96EC6F8B7558C014977BD00480010D89D106240803518E31C4230052C01786F272FF354C8D4D437DF52BC2C300567066550A2A900427E0084C254739FB8E080111E0
|
||||||
284
src/bin/day16.rs
Normal file
284
src/bin/day16.rs
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
const REAL_DATA: &str = include_str!("../../data/day16.txt");
|
||||||
|
|
||||||
|
#[derive(Debug, Error, PartialEq)]
|
||||||
|
enum Oopsie {
|
||||||
|
#[error("Bad digit in input: '{0}'")]
|
||||||
|
BadDigit(char),
|
||||||
|
#[error("Ran out of bits (looking for {1}) pulling value for type {0}")]
|
||||||
|
RanOuttaBits(&'static str, usize),
|
||||||
|
#[error("Invalid split attempt: stream length is {0}, but requested split at {1}")]
|
||||||
|
InvalidSplit(usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BitStream {
|
||||||
|
bits: VecDeque<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for BitStream {
|
||||||
|
type Err = Oopsie;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut bits = VecDeque::with_capacity(s.len() * 4);
|
||||||
|
|
||||||
|
for c in s.chars() {
|
||||||
|
let value = c.to_digit(16).ok_or(Oopsie::BadDigit(c))?;
|
||||||
|
bits.push_back(value & 0b1000 > 0);
|
||||||
|
bits.push_back(value & 0b0100 > 0);
|
||||||
|
bits.push_back(value & 0b0010 > 0);
|
||||||
|
bits.push_back(value & 0b0001 > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(BitStream { bits })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! next_chunk {
|
||||||
|
($id: ident, $t: ty) => {
|
||||||
|
fn $id(&mut self, bits: usize) -> Result<$t, Oopsie> {
|
||||||
|
let mut res = 0;
|
||||||
|
|
||||||
|
for _ in 0..bits {
|
||||||
|
res <<= 1;
|
||||||
|
match self.bits.pop_front() {
|
||||||
|
None => return Err(Oopsie::RanOuttaBits(stringify!($t), bits)),
|
||||||
|
Some(true) => res += 1,
|
||||||
|
Some(false) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitStream {
|
||||||
|
fn next_bit(&mut self) -> Result<bool, Oopsie> {
|
||||||
|
self.bits.pop_front().ok_or(Oopsie::RanOuttaBits("bool", 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
next_chunk!(next_u8, u8);
|
||||||
|
next_chunk!(next_u16, u16);
|
||||||
|
next_chunk!(next_u64, u64);
|
||||||
|
|
||||||
|
fn take(&mut self, size: usize) -> Result<BitStream, Oopsie> {
|
||||||
|
if self.bits.len() < size {
|
||||||
|
Err(Oopsie::InvalidSplit(self.bits.len(), size))
|
||||||
|
} else {
|
||||||
|
let rest = self.bits.split_off(size);
|
||||||
|
let retval = self.bits.clone();
|
||||||
|
self.bits = rest;
|
||||||
|
|
||||||
|
Ok(BitStream { bits: retval })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.bits.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum Message {
|
||||||
|
Literal(u8, u64),
|
||||||
|
Sum(u8, Vec<Message>),
|
||||||
|
Product(u8, Vec<Message>),
|
||||||
|
Minimum(u8, Vec<Message>),
|
||||||
|
Maximum(u8, Vec<Message>),
|
||||||
|
GreaterThan(u8, Vec<Message>),
|
||||||
|
LessThan(u8, Vec<Message>),
|
||||||
|
EqualTo(u8, Vec<Message>),
|
||||||
|
Sequence(u8, u8, Vec<Message>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message {
|
||||||
|
fn sequence(version: u8, type_id: u8, sequence: Vec<Message>) -> Message {
|
||||||
|
match type_id {
|
||||||
|
0 => Message::Sum(version, sequence),
|
||||||
|
1 => Message::Product(version, sequence),
|
||||||
|
2 => Message::Minimum(version, sequence),
|
||||||
|
3 => Message::Maximum(version, sequence),
|
||||||
|
5 if sequence.len() == 2 => Message::GreaterThan(version, sequence),
|
||||||
|
6 if sequence.len() == 2 => Message::LessThan(version, sequence),
|
||||||
|
7 if sequence.len() == 2 => Message::EqualTo(version, sequence),
|
||||||
|
_ => Message::Sequence(version, type_id, sequence),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version_sum(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Message::Literal(x, _) => *x as usize,
|
||||||
|
Message::Sum(x, seq) => {
|
||||||
|
(*x as usize) + seq.iter().map(|v| v.version_sum()).sum::<usize>()
|
||||||
|
}
|
||||||
|
Message::Product(x, seq) => {
|
||||||
|
(*x as usize) + seq.iter().map(|v| v.version_sum()).sum::<usize>()
|
||||||
|
}
|
||||||
|
Message::Minimum(x, seq) => {
|
||||||
|
(*x as usize) + seq.iter().map(|v| v.version_sum()).sum::<usize>()
|
||||||
|
}
|
||||||
|
Message::Maximum(x, seq) => {
|
||||||
|
(*x as usize) + seq.iter().map(|v| v.version_sum()).sum::<usize>()
|
||||||
|
}
|
||||||
|
Message::GreaterThan(x, seq) => {
|
||||||
|
(*x as usize) + seq.iter().map(|v| v.version_sum()).sum::<usize>()
|
||||||
|
}
|
||||||
|
Message::LessThan(x, seq) => {
|
||||||
|
(*x as usize) + seq.iter().map(|v| v.version_sum()).sum::<usize>()
|
||||||
|
}
|
||||||
|
Message::EqualTo(x, seq) => {
|
||||||
|
(*x as usize) + seq.iter().map(|v| v.version_sum()).sum::<usize>()
|
||||||
|
}
|
||||||
|
Message::Sequence(x, _, seq) => {
|
||||||
|
(*x as usize) + seq.iter().map(|v| v.version_sum()).sum::<usize>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Message::Literal(_, x) => *x,
|
||||||
|
Message::Sum(_, seq) => seq.iter().map(|x| x.eval()).sum(),
|
||||||
|
Message::Product(_, seq) => seq.iter().map(|x| x.eval()).product(),
|
||||||
|
Message::Minimum(_, seq) => seq.iter().map(|x| x.eval()).min().unwrap(),
|
||||||
|
Message::Maximum(_, seq) => seq.iter().map(|x| x.eval()).max().unwrap(),
|
||||||
|
Message::GreaterThan(_, seq) => {
|
||||||
|
if seq[0].eval() > seq[1].eval() {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::LessThan(_, seq) => {
|
||||||
|
if seq[0].eval() < seq[1].eval() {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::EqualTo(_, seq) => {
|
||||||
|
if seq[0].eval() == seq[1].eval() {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::Sequence(_, _, _) => panic!("Tried to evaluate unknown sequence!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a mut BitStream> for Message {
|
||||||
|
type Error = Oopsie;
|
||||||
|
|
||||||
|
fn try_from(value: &mut BitStream) -> Result<Self, Self::Error> {
|
||||||
|
let version = value.next_u8(3)?;
|
||||||
|
let type_id = value.next_u8(3)?;
|
||||||
|
|
||||||
|
if type_id == 4 {
|
||||||
|
let mut literal = 0u64;
|
||||||
|
let mut keep_going = true;
|
||||||
|
|
||||||
|
while keep_going {
|
||||||
|
keep_going = value.next_bit()?;
|
||||||
|
literal = (literal << 4) + value.next_u64(4)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Message::Literal(version, literal))
|
||||||
|
} else {
|
||||||
|
let length_type_id = value.next_bit()?;
|
||||||
|
let mut seq = Vec::new();
|
||||||
|
|
||||||
|
if length_type_id {
|
||||||
|
let subpart_count = value.next_u16(11)?;
|
||||||
|
|
||||||
|
for _ in 0..subpart_count {
|
||||||
|
seq.push(Message::try_from(&mut *value)?);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let subpart_len = value.next_u16(15)? as usize;
|
||||||
|
let mut my_bits = value.take(subpart_len)?;
|
||||||
|
|
||||||
|
while !my_bits.is_empty() {
|
||||||
|
seq.push(Message::try_from(&mut my_bits)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Message::sequence(version, type_id, seq))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Message {
|
||||||
|
type Err = Oopsie;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut bits = BitStream::from_str(s)?;
|
||||||
|
Message::try_from(&mut bits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_parsing() {
|
||||||
|
assert_eq!(
|
||||||
|
Ok(Message::Literal(6, 2021)),
|
||||||
|
Message::try_from(&mut BitStream::from_str("D2FE28").unwrap())
|
||||||
|
);
|
||||||
|
assert_eq!(Ok(Message::Literal(6, 2021)), Message::from_str("D2FE28"));
|
||||||
|
assert_eq!(
|
||||||
|
Ok(Message::LessThan(
|
||||||
|
1,
|
||||||
|
vec![Message::Literal(6, 10), Message::Literal(2, 20)]
|
||||||
|
)),
|
||||||
|
Message::from_str("38006F45291200")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Ok(Message::Maximum(
|
||||||
|
7,
|
||||||
|
vec![
|
||||||
|
Message::Literal(2, 1),
|
||||||
|
Message::Literal(4, 2),
|
||||||
|
Message::Literal(1, 3)
|
||||||
|
]
|
||||||
|
)),
|
||||||
|
Message::from_str("EE00D40C823060")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn example_tests() {
|
||||||
|
assert_eq!(
|
||||||
|
16,
|
||||||
|
Message::from_str("8A004A801A8002F478")
|
||||||
|
.unwrap()
|
||||||
|
.version_sum()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
12,
|
||||||
|
Message::from_str("620080001611562C8802118E34")
|
||||||
|
.unwrap()
|
||||||
|
.version_sum()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
23,
|
||||||
|
Message::from_str("C0015000016115A2E0802F182340")
|
||||||
|
.unwrap()
|
||||||
|
.version_sum()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
31,
|
||||||
|
Message::from_str("A0016C880162017C3686B18A3D4780")
|
||||||
|
.unwrap()
|
||||||
|
.version_sum()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Oopsie> {
|
||||||
|
let message = Message::from_str(REAL_DATA)?;
|
||||||
|
println!("Version sum: {}", message.version_sum());
|
||||||
|
println!("Input computed value: {}", message.eval());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
128
src/bin/day17.rs
Normal file
128
src/bin/day17.rs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
use std::cmp::{max, Ordering};
|
||||||
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
|
struct TargetArea {
|
||||||
|
target_x: RangeInclusive<isize>,
|
||||||
|
target_y: RangeInclusive<isize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TargetArea {
|
||||||
|
fn contains(&self, x: isize, y: isize) -> bool {
|
||||||
|
self.target_x.contains(&x) && self.target_y.contains(&y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Probe {
|
||||||
|
pos_x: isize,
|
||||||
|
pos_y: isize,
|
||||||
|
vel_x: isize,
|
||||||
|
vel_y: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum Miss {
|
||||||
|
MissedShort,
|
||||||
|
MissedLong,
|
||||||
|
Skipped,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Probe {
|
||||||
|
fn new(vel_x: isize, vel_y: isize) -> Probe {
|
||||||
|
Probe {
|
||||||
|
pos_x: 0,
|
||||||
|
pos_y: 0,
|
||||||
|
vel_x,
|
||||||
|
vel_y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step(&mut self) {
|
||||||
|
self.pos_x += self.vel_x;
|
||||||
|
self.pos_y += self.vel_y;
|
||||||
|
|
||||||
|
match self.vel_x.cmp(&0) {
|
||||||
|
Ordering::Equal => {}
|
||||||
|
Ordering::Greater => self.vel_x -= 1,
|
||||||
|
Ordering::Less => self.vel_x += 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.vel_y -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hits_target(&mut self, target: &TargetArea) -> Result<(isize, isize, isize), Miss> {
|
||||||
|
let mut max_y = self.pos_y;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
max_y = max(self.pos_y, max_y);
|
||||||
|
|
||||||
|
if target.contains(self.pos_x, self.pos_y) {
|
||||||
|
return Ok((self.pos_x, self.pos_y, max_y));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.vel_x == 0 && &self.pos_y < target.target_y.end() {
|
||||||
|
if &self.pos_x < target.target_x.start() {
|
||||||
|
return Err(Miss::MissedShort);
|
||||||
|
}
|
||||||
|
|
||||||
|
if &self.pos_x > target.target_x.end() {
|
||||||
|
return Err(Miss::MissedLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(Miss::Skipped);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.step();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn highest_hit(target: &TargetArea) -> (isize, isize, isize, usize) {
|
||||||
|
let mut vel_x = 1;
|
||||||
|
let mut vel_y = 1;
|
||||||
|
let mut max_y = 0;
|
||||||
|
let mut count = 0;
|
||||||
|
|
||||||
|
for attempt_x in 1..250 {
|
||||||
|
for attempt_y in -250..250 {
|
||||||
|
let mut probe = Probe::new(attempt_x, attempt_y);
|
||||||
|
|
||||||
|
if let Ok((x, y, new_max_y)) = probe.hits_target(target) {
|
||||||
|
println!(
|
||||||
|
"Hit with <{},{}> at ({},{}) with highest y {}",
|
||||||
|
attempt_x, attempt_y, x, y, new_max_y
|
||||||
|
);
|
||||||
|
if new_max_y > max_y {
|
||||||
|
vel_x = attempt_x;
|
||||||
|
vel_y = attempt_y;
|
||||||
|
max_y = new_max_y;
|
||||||
|
}
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(vel_x, vel_y, max_y, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let test_target = TargetArea {
|
||||||
|
target_x: 20..=30,
|
||||||
|
target_y: -10..=-5,
|
||||||
|
};
|
||||||
|
let mut test_probe = Probe::new(7, 2);
|
||||||
|
|
||||||
|
assert_eq!(Ok((28, -7, 3)), test_probe.hits_target(&test_target));
|
||||||
|
assert_eq!((6, 9, 45, 112), highest_hit(&test_target));
|
||||||
|
|
||||||
|
let real_target = TargetArea {
|
||||||
|
target_x: 111..=161,
|
||||||
|
target_y: -154..=-101,
|
||||||
|
};
|
||||||
|
println!(
|
||||||
|
"target_y: {:?} (start {}, end {})",
|
||||||
|
real_target.target_y,
|
||||||
|
real_target.target_y.start(),
|
||||||
|
real_target.target_y.end()
|
||||||
|
);
|
||||||
|
println!("Search results answer: {:?}", highest_hit(&real_target));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user