Skip to content

Commit

Permalink
Year 2017: Day 18
Browse files Browse the repository at this point in the history
  • Loading branch information
joshleaves committed Mar 31, 2024
1 parent 35bf6ec commit da748f1
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 3 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Of note:
- The changelog 2015.5.2 has been rewritten from each commit content.
- This file may be amended entirely in the future to adhere to the [GNU Changelog style](https://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html#Style-of-Change-Logs)

## [2017.18.1]
### Added
- Solved [exercice for 2017, day 18](src/year_2017/18.rs).

## [2017.17.2]
### Removed
- Removed benchmarks for Year 2016, day 04.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "advent-rs"
version = "2017.17.2"
version = "2017.18.1"
edition = "2021"
authors = ["Arnaud 'red' Rouyer"]
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ As I said, [Consistency is hard](https://github.com/joshleaves/advent-rb), and I
I'm also adding notes that may be useful if you're (like me) discovering Rust:
- [2015, complete!](NOTES_2015.md)
- [2016, complete!](NOTES_2016.md)
- [2017, up to day 17](NOTES_2017.md)
- [2017, up to day 18](NOTES_2017.md)

# Regarding style rules
I'm gonna use a mix of what `cargo fmt` does, with some stuff that feels more natural to me.
Expand Down
19 changes: 18 additions & 1 deletion benches/year_2017.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use advent_rs::year_2017::day_14;
use advent_rs::year_2017::day_15;
use advent_rs::year_2017::day_16;
use advent_rs::year_2017::day_17;
use advent_rs::year_2017::day_18;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn year_2017_day_01(c: &mut Criterion) {
Expand Down Expand Up @@ -275,6 +276,21 @@ fn year_2017_day_17(c: &mut Criterion) {
g2017_day_17.finish();
}

fn year_2017_day_18(c: &mut Criterion) {
let input_day_18 = include_str!("../inputs/year_2017/day_18_input");
assert_eq!(day_18::day_18_v1(input_day_18), 1_187);
assert_eq!(day_18::day_18_v2(input_day_18), 5_969);

let mut g2017_day_18 = c.benchmark_group("year_2017::day_18");
g2017_day_18.bench_function("year_2017::day_18_v1", |b| {
b.iter(|| day_18::day_18_v1(black_box(input_day_18)))
});
g2017_day_18.bench_function("year_2017::day_18_v2", |b| {
b.iter(|| day_18::day_18_v2(black_box(input_day_18)))
});
g2017_day_18.finish();
}

criterion_group!(
benches,
year_2017_day_01,
Expand All @@ -293,6 +309,7 @@ criterion_group!(
year_2017_day_14,
year_2017_day_15,
year_2017_day_16,
year_2017_day_17
year_2017_day_17,
year_2017_day_18
);
criterion_main!(benches);
41 changes: 41 additions & 0 deletions inputs/year_2017/day_18_input
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
set i 31
set a 1
mul p 17
jgz p p
mul a 2
add i -1
jgz i -2
add a -1
set i 127
set p 464
mul p 8505
mod p a
mul p 129749
add p 12345
mod p a
set b p
mod b 10000
snd b
add i -1
jgz i -9
jgz a 3
rcv b
jgz b -1
set f 0
set i 126
rcv a
rcv b
set p a
mul p -1
add p b
jgz p 4
snd a
set a b
jgz 1 3
snd b
set f 1
add i -1
jgz i -11
snd a
jgz f -16
jgz a -19
9 changes: 9 additions & 0 deletions src/year_2017.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub mod day_14;
pub mod day_15;
pub mod day_16;
pub mod day_17;
pub mod day_18;

pub fn solve(day: u8, part: u8, input: impl Into<String>) -> Option<String> {
if part > 2 {
Expand All @@ -45,6 +46,7 @@ pub fn solve(day: u8, part: u8, input: impl Into<String>) -> Option<String> {
15 => Some(day_15::day_15(part, input).to_string()),
16 => Some(day_16::day_16(part, input).to_string()),
17 => Some(day_17::day_17(part, input).to_string()),
18 => Some(day_18::day_18(part, input).to_string()),
_ => None,
}
}
Expand Down Expand Up @@ -171,4 +173,11 @@ mod tests {
assert_eq!(day_17::day_17_v1(input), 1_173);
assert_eq!(day_17::day_17_v2(input), 1_930_815);
}

#[test]
fn day_18() {
let input = include_str!("../inputs/year_2017/day_18_input");
assert_eq!(day_18::day_18_v1(input), 1_187);
assert_eq!(day_18::day_18_v2(input), 5_969);
}
}
198 changes: 198 additions & 0 deletions src/year_2017/day_18.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
use itertools::Itertools;

#[derive(Clone, Debug)]
enum VoR {
Value(i64),
Register(usize),
}

impl VoR {
fn new(input: &str) -> Self {
let bytes = input.as_bytes();
match bytes[0] {
b'a'..=b'z' => VoR::Register((bytes[0] - b'a') as usize),
b'0'..=b'9' | b'-' => VoR::Value(input.parse::<i64>().unwrap()),
_ => panic!("Invalid value: _{}_", input),
}
}
}

#[derive(Clone, Debug)]
enum Instruction {
Sound(VoR),
Set(usize, VoR),
Add(usize, VoR),
Mul(usize, VoR),
Mod(usize, VoR),
Recover(usize),
Jump(VoR, VoR),
}

impl Instruction {
fn new_snd(vor: &str) -> Self {
Instruction::Sound(VoR::new(vor))
}

fn new_set(reg: &str, vor: &str) -> Self {
Instruction::Set((reg.as_bytes()[0] - b'a') as usize, VoR::new(vor))
}

fn new_add(reg: &str, vor: &str) -> Self {
Instruction::Add((reg.as_bytes()[0] - b'a') as usize, VoR::new(vor))
}

fn new_mul(reg: &str, vor: &str) -> Self {
Instruction::Mul((reg.as_bytes()[0] - b'a') as usize, VoR::new(vor))
}

fn new_mod(reg: &str, vor: &str) -> Self {
Instruction::Mod((reg.as_bytes()[0] - b'a') as usize, VoR::new(vor))
}

fn new_rcv(reg: &str) -> Self {
Instruction::Recover((reg.as_bytes()[0] - b'a') as usize)
}

fn new_jgz(vor: &str, jmp: &str) -> Self {
Instruction::Jump(VoR::new(vor), VoR::new(jmp))
}

fn new(input: &str) -> Self {
let mut parts = input.split_whitespace();
match parts.next().unwrap() {
"snd" => Self::new_snd(parts.next().unwrap()),
"set" => Self::new_set(parts.next().unwrap(), parts.next().unwrap()),
"add" => Self::new_add(parts.next().unwrap(), parts.next().unwrap()),
"mul" => Self::new_mul(parts.next().unwrap(), parts.next().unwrap()),
"mod" => Self::new_mod(parts.next().unwrap(), parts.next().unwrap()),
"rcv" => Self::new_rcv(parts.next().unwrap()),
"jgz" => Self::new_jgz(parts.next().unwrap(), parts.next().unwrap()),
_ => panic!("Invalid instruction: {}", input),
}
}
}

#[derive(Clone)]
struct SoundCard {
program_id: i64,
registers: Vec<i64>,
instructions: Vec<Instruction>,
pc: i64,
outputs: Vec<i64>,
inputs: Vec<i64>,
}

impl SoundCard {
pub fn new(input: &str, program_id: i64) -> Self {
let registers = vec![0; 26];
let instructions = input.lines().map(Instruction::new).collect_vec();

SoundCard {
program_id,
registers,
instructions,
pc: 0,
outputs: vec![],
inputs: vec![],
}
}

fn value_of(&self, vor: &VoR) -> i64 {
match vor {
VoR::Value(value) => *value,
VoR::Register(value) => self.registers[*value],
}
}

fn execute_once(&mut self) -> bool {
if self.pc < 0 || self.pc >= self.instructions.len() as i64 {
return false;
}
let instruction = &self.instructions[self.pc as usize];
match instruction {
Instruction::Sound(vor) => self.outputs.push(self.value_of(vor)),
Instruction::Set(reg, vor) => self.registers[*reg] = self.value_of(vor),
Instruction::Add(reg, vor) => self.registers[*reg] += self.value_of(vor),
Instruction::Mul(reg, vor) => self.registers[*reg] *= self.value_of(vor),
Instruction::Mod(reg, vor) => self.registers[*reg] %= self.value_of(vor),
Instruction::Recover(reg) => match (self.program_id, self.registers[*reg]) {
(-1, 0) => return false,
(-1, _) => (),
(_, _) => {
if self.inputs.is_empty() {
return false;
} else {
self.registers[*reg] = self.inputs.remove(0);
}
}
},
Instruction::Jump(vor, jmp) => {
if self.value_of(vor) > 0 {
self.pc = self.pc + self.value_of(jmp) - 1;
}
}
}
self.pc += 1;
true
}

pub fn get_first_sound(&mut self) -> i64 {
while self.execute_once() {}
*self.outputs.iter().last().unwrap()
}
}

pub fn day_18_v1(input: impl Into<String>) -> i64 {
let mut sound_card = SoundCard::new(&input.into(), 0);
sound_card.get_first_sound()
}

pub fn day_18_v2(input: impl Into<String>) -> i64 {
let input = input.into();
let mut sc0 = SoundCard::new(&input, 0);
let mut sc1 = SoundCard::new(&input, 1);
sc1.registers[(b'p' - b'a') as usize] = 1;
let mut sound_count = 0;
while sc0.execute_once() || sc1.execute_once() {
let mut output_1 = sc1.outputs.drain(..).collect_vec();
sound_count += output_1.len() as i64;
sc0.inputs.append(&mut output_1);

sc1.inputs.append(&mut sc0.outputs.drain(..).collect_vec());
}
sound_count
// sc1.sent_count
}
solvable!(day_18, day_18_v1, day_18_v2, i64);

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn works_with_samples_v1() {
let sample_one: &str = "set a 1\n\
add a 2\n\
mul a a\n\
mod a 5\n\
snd a\n\
set a 0\n\
rcv a\n\
jgz a -1\n\
set a 1\n\
jgz a -2";
assert_eq!(day_18_v1(sample_one), 4);
}

#[test]
fn works_with_samples_v2() {
let sample_two = "snd 1\n\
snd 2\n\
snd p\n\
rcv a\n\
rcv b\n\
rcv c\n\
rcv d";
assert_eq!(day_18_v2(sample_two), 3);
}
}

0 comments on commit da748f1

Please sign in to comment.