I don't think I'm going to catch up in time. :(

This commit is contained in:
2021-12-25 17:13:16 -08:00
parent 1f286b293e
commit e97b7a4c33
3 changed files with 413 additions and 0 deletions

1
data/day16.txt Normal file
View File

@@ -0,0 +1 @@
C20D718021600ACDC372CD8DE7A057252A49C940239D68978F7970194EA7CCB310088760088803304A0AC1B100721EC298D3307440041CD8B8005D12DFD27CBEEF27D94A4E9B033006A45FE71D665ACC0259C689B1F99679F717003225900465800804E39CE38CE161007E52F1AEF5EE6EC33600BCC29CFFA3D8291006A92CA7E00B4A8F497E16A675EFB6B0058F2D0BD7AE1371DA34E730F66009443C00A566BFDBE643135FEDF321D000C6269EA66545899739ADEAF0EB6C3A200B6F40179DE31CB7B277392FA1C0A95F6E3983A100993801B800021B0722243D00042E0DC7383D332443004E463295176801F29EDDAA853DBB5508802859F2E9D2A9308924F9F31700AA4F39F720C733A669EC7356AC7D8E85C95E123799D4C44C0109C0AF00427E3CC678873F1E633C4020085E60D340109E3196023006040188C910A3A80021B1763FC620004321B4138E52D75A20096E4718D3E50016B19E0BA802325E858762D1802B28AD401A9880310E61041400043E2AC7E8A4800434DB24A384A4019401C92C154B43595B830002BC497ED9CC27CE686A6A43925B8A9CFFE3A9616E5793447004A4BBB749841500B26C5E6E306899C5B4C70924B77EF254B48688041CD004A726ED3FAECBDB2295AEBD984E08E0065C101812E006380126005A80124048CB010D4C03DC900E16A007200B98E00580091EE004B006902004B00410000AF00015933223100688010985116A311803D05E3CC4B300660BC7283C00081CF26491049F3D690E9802739661E00D400010A8B91F2118803310A2F43396699D533005E37E8023311A4BB9961524A4E2C027EC8C6F5952C2528B333FA4AD386C0A56F39C7DB77200C92801019E799E7B96EC6F8B7558C014977BD00480010D89D106240803518E31C4230052C01786F272FF354C8D4D437DF52BC2C300567066550A2A900427E0084C254739FB8E080111E0

284
src/bin/day16.rs Normal file
View 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
View 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));
}