Compare commits
1 commit
df5e614d14
...
2f48b0e912
Author | SHA1 | Date | |
---|---|---|---|
Tristan Daniël Maat | 2f48b0e912 |
|
@ -1,6 +1,5 @@
|
||||||
use std::convert::TryFrom;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::iter;
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
@ -10,22 +9,26 @@ const MASK_36_BITS: u64 = 0xFFFFFFFFF;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let input = fs::read_to_string("input")?;
|
let input = fs::read_to_string("input")?;
|
||||||
let program = parse_program_chip1(&input)?;
|
let program = parse_program(&input)?;
|
||||||
|
|
||||||
// Part 1
|
// Part 1
|
||||||
let memory = run_program_chip1(&program);
|
let memory = run_program_chip1(&program);
|
||||||
println!("{}", memory.iter().map(|(_, value)| value).sum::<u64>());
|
println!("{}", memory.iter().map(|(_, value)| value).sum::<u64>());
|
||||||
|
|
||||||
|
// Part 2
|
||||||
|
let memory = run_program_chip2(&program);
|
||||||
|
println!("{}", memory.iter().map(|(_, value)| value).sum::<u64>());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
enum Chip1Instruction {
|
enum Instruction {
|
||||||
Mask(u64, u64),
|
Mask(u64, u64, u64),
|
||||||
Mem(usize, u64),
|
Mem(u64, u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chip1Instruction {
|
impl Instruction {
|
||||||
fn new(instruction: &str) -> Result<Self, String> {
|
fn new(instruction: &str) -> Result<Self, String> {
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref INSTRUCTION_RE: Regex =
|
static ref INSTRUCTION_RE: Regex =
|
||||||
|
@ -44,7 +47,7 @@ impl Chip1Instruction {
|
||||||
let or = u64::from_str_radix(&caps[3].replace("X", "0"), 2)
|
let or = u64::from_str_radix(&caps[3].replace("X", "0"), 2)
|
||||||
.map_err(|e| format!("Could not parse or: {}", e))?;
|
.map_err(|e| format!("Could not parse or: {}", e))?;
|
||||||
|
|
||||||
Chip1Instruction::Mask(and, or)
|
Instruction::Mask(and, or, and ^ or)
|
||||||
}
|
}
|
||||||
"mem" => {
|
"mem" => {
|
||||||
let location = caps[2]
|
let location = caps[2]
|
||||||
|
@ -54,30 +57,28 @@ impl Chip1Instruction {
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|e| format!("Could not parse value: {}", e))?;
|
.map_err(|e| format!("Could not parse value: {}", e))?;
|
||||||
|
|
||||||
Chip1Instruction::Mem(location, value)
|
Instruction::Mem(location, value)
|
||||||
}
|
}
|
||||||
_ => Err("Invalid instruction")?,
|
_ => Err("Invalid instruction")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_program_chip1(input: &str) -> Result<Vec<Chip1Instruction>, String> {
|
fn parse_program(input: &str) -> Result<Vec<Instruction>, String> {
|
||||||
input.lines().map(Chip1Instruction::new).collect()
|
input.lines().map(Instruction::new).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_program_chip1(program: &Vec<Chip1Instruction>) -> Vec<(u64, u64)> {
|
fn run_program_chip1(program: &Vec<Instruction>) -> Vec<(u64, u64)> {
|
||||||
let mut mask = (u64::max_value() & MASK_36_BITS, 0);
|
let mut mask = (u64::max_value() & MASK_36_BITS, 0);
|
||||||
|
|
||||||
program
|
program
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|instruction| match instruction {
|
.filter_map(|instruction| match instruction {
|
||||||
Chip1Instruction::Mask(and, or) => {
|
Instruction::Mask(and, or, _) => {
|
||||||
mask = (*and, *or);
|
mask = (*and, *or);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Chip1Instruction::Mem(address, value) => {
|
Instruction::Mem(address, value) => Some((*address as u64, value & mask.0 | mask.1)),
|
||||||
Some((*address as u64, value & mask.0 | mask.1))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<(u64, u64)>>()
|
.collect::<Vec<(u64, u64)>>()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -86,86 +87,36 @@ fn run_program_chip1(program: &Vec<Chip1Instruction>) -> Vec<(u64, u64)> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
fn run_program_chip2(program: &Vec<Instruction>) -> Vec<(u64, u64)> {
|
||||||
enum Chip2Instruction {
|
let mut memory = HashMap::new();
|
||||||
Mask(u64, u64),
|
let mut mask = (0, 0);
|
||||||
Mem(usize, u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Chip2Instruction {
|
for instruction in program {
|
||||||
fn new(instruction: &str) -> Result<Self, String> {
|
match instruction {
|
||||||
lazy_static! {
|
Instruction::Mask(_, or, floating) => {
|
||||||
static ref INSTRUCTION_RE: Regex =
|
mask = (*or, *floating);
|
||||||
Regex::new(r"([[:alpha:]]+?)\[?(\d+?)?\]? = ([0-9X]+)")
|
}
|
||||||
.expect("Regex should compile");
|
Instruction::Mem(address, value) => {
|
||||||
|
// We set floating bits to 0 here, so that they can be
|
||||||
|
// toggled with addition
|
||||||
|
let base = (address | mask.0) & !mask.1 & MASK_36_BITS;
|
||||||
|
|
||||||
|
let mut floating = !mask.1 & MASK_36_BITS;
|
||||||
|
while floating <= MASK_36_BITS {
|
||||||
|
// For each unset bit in the floating mask, we
|
||||||
|
// toggle the bit in the address
|
||||||
|
memory.insert(base | (!floating & MASK_36_BITS), *value);
|
||||||
|
floating += 1;
|
||||||
|
|
||||||
|
// Since add does overflows, we reset 1s to 0s if
|
||||||
|
// they're unset in the original mask
|
||||||
|
floating |= !mask.1 & MASK_36_BITS;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let caps = INSTRUCTION_RE
|
|
||||||
.captures(instruction)
|
|
||||||
.ok_or("Instruction did not match")?;
|
|
||||||
|
|
||||||
Ok(match &caps[1] {
|
|
||||||
"mask" => {
|
|
||||||
let or = u64::from_str_radix(&caps[3].replace("X", "0"), 2)
|
|
||||||
.map_err(|e| format!("Could not parse or: {}", e))?;
|
|
||||||
|
|
||||||
let floating = u64::from_str_radix(&caps[3].replace("X", "0"), 2)
|
|
||||||
.map_err(|e| format!("Could not parse floating: {}", e))?;
|
|
||||||
|
|
||||||
Chip2Instruction::Mask(or, floating)
|
|
||||||
}
|
|
||||||
"mem" => {
|
|
||||||
let location = caps[2]
|
|
||||||
.parse()
|
|
||||||
.map_err(|e| format!("Could not parse location: {}", e))?;
|
|
||||||
let value = caps[3]
|
|
||||||
.parse()
|
|
||||||
.map_err(|e| format!("Could not parse value: {}", e))?;
|
|
||||||
|
|
||||||
Chip2Instruction::Mem(location, value)
|
|
||||||
}
|
|
||||||
_ => Err("Invalid instruction")?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_program_chip2(input: &str) -> Result<Vec<Chip2Instruction>, String> {
|
memory.drain().collect()
|
||||||
input.lines().map(Chip2Instruction::new).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_program_chip2(program: &Vec<Chip2Instruction>) -> Vec<(u64, u64)> {
|
|
||||||
let mut mask = (
|
|
||||||
u64::max_value() & MASK_36_BITS,
|
|
||||||
u64::max_value() & MASK_36_BITS,
|
|
||||||
);
|
|
||||||
|
|
||||||
program
|
|
||||||
.iter()
|
|
||||||
.filter_map(|instruction| match instruction {
|
|
||||||
Chip2Instruction::Mask(and, or) => {
|
|
||||||
mask = (*and, *or);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Chip2Instruction::Mem(address, value) => Some(
|
|
||||||
mask.1
|
|
||||||
.iter()
|
|
||||||
.flat_map(|index| {
|
|
||||||
let address = u64::try_from(*address).expect("<=64 bit usize required");
|
|
||||||
|
|
||||||
iter::once((address | 1 << index | mask.0) & MASK_36_BITS).chain(
|
|
||||||
iter::once((address & !(1 << index) | mask.0) & MASK_36_BITS),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.map(|address| (address, *value))
|
|
||||||
.collect::<Vec<(u64, u64)>>(),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
.collect::<Vec<(u64, u64)>>()
|
|
||||||
.into_iter()
|
|
||||||
.rev()
|
|
||||||
.unique_by(|address| address.0)
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -184,7 +135,7 @@ mod tests {
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
let program = parse_program_chip1(input)?;
|
let program = parse_program(input)?;
|
||||||
let memory = run_program_chip1(&program);
|
let memory = run_program_chip1(&program);
|
||||||
|
|
||||||
assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 165);
|
assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 165);
|
||||||
|
@ -202,7 +153,7 @@ mod tests {
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
let program = parse_program_chip2(input)?;
|
let program = parse_program(input)?;
|
||||||
let memory = dbg!(run_program_chip2(&program));
|
let memory = dbg!(run_program_chip2(&program));
|
||||||
|
|
||||||
assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 208);
|
assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 208);
|
||||||
|
|
Loading…
Reference in a new issue