Day 07: Some Assembly Required

It seems there is always a plethora or “wires” problems and I always manage to make a hacky solution. I should create a standard “wires” library to handle all of the wires problems that arise. The problem might be solved quicker by finding dependencies and working backwards but a brute force approach works well enough.

Loading code...

use std::collections::HashMap;

// TODO this whole process should be cleaner and solved for more generic "wire" problems
// Perhaps it's best to model it as a directed graph? Maybe even make its own library

#[derive(Debug, PartialEq)]
enum Action {
	Assignment,
	Not,
	And,
	Or,
	Rshift,
	Lshift
}

#[derive(Debug)]
struct Rule {
	from_a: Option<String>,
	from_b: Option<String>,
	to: String,
	action: Action
}

fn parse_input (input: &str) -> Vec<Rule> {
	return input.lines()
		.map(|line| line.split(" ").map(|x| String::from(x)).collect::<Vec<_>>())
		.map(|line| (
			if line.len() == 3 { // Direct assignment
				return Rule {
					from_a: Some(line[0].clone()),
					from_b: None,
					to: line[2].clone(),
					action: Action::Assignment
				};
			} else if line.len() == 4 { // Not
				return Rule {
					from_a: Some(line[1].clone()),
					from_b: None,
					to: line[3].clone(),
					action: Action::Not
				};
			} else {
				return Rule {
					from_a: Some(line[0].clone()),
					from_b: Some(line[2].clone()),
					to: line[4].clone(),
					action: match line[1].as_str() {
						"AND" => Action::And,
						"OR" => Action::Or,
						"RSHIFT" => Action::Rshift,
						"LSHIFT" => Action::Lshift,
						_ => panic!()
					}
				};
			}

		))
		.collect::<Vec<_>>();
}

fn parse_or_lookup (map: &HashMap<String, u16>, key: String) -> Option<u16> {
	match u16::from_str_radix(key.as_str(), 10) {
		Ok(x) => Some(x),
		Err(_) => map.get(&key).copied()
	}
}

fn apply_rules (machine_state: &mut HashMap<String, u16>, rules: &Vec<Rule>) {
	let mut applied_rules = vec![false; rules.len()];

	loop {
		let mut some_rule_changed = false;

		for i in 0..applied_rules.len() {
			if applied_rules[i] == true { continue; }
			let rule = &rules[i];

			let from_a = {
				let from_a = parse_or_lookup(&machine_state, rule.from_a.clone().unwrap());
				if from_a.is_none() { continue; } // from_a is always required
				from_a.unwrap()
			};

			let from_b = if rule.action == Action::Assignment || rule.action == Action::Not {
				None
			} else {
				let from_b = parse_or_lookup(&machine_state, rule.from_b.clone().unwrap());
				if from_b.is_none() { continue; } // from_b is required for double rules
				from_b
			};
			
			let to = parse_or_lookup(&machine_state, rule.to.clone());
			if to.is_some() { continue; } // Prevent accidental overwrites
			
			let result = match rule.action {
				Action::Assignment => from_a,
				Action::Not => !from_a,
				Action::And => from_a & from_b.unwrap(),
				Action::Or => from_a | from_b.unwrap(),
				Action::Rshift => from_a >> from_b.unwrap(),
				Action::Lshift => from_a << from_b.unwrap()
			};

			machine_state.insert(rule.to.clone(), result);
			applied_rules[i] = true;
			some_rule_changed = true;
		}

		if some_rule_changed == false { break; }
	}
}

pub fn day_07_1 (input: &str) -> String {
	let rules = parse_input(input);
	let mut machine_state = HashMap::new();
	apply_rules(&mut machine_state, &rules);
	return machine_state.get("a").unwrap().to_string();
}

pub fn day_07_2 (input: &str) -> String {
	let rules = parse_input(input);
	
	// Run the first part
	let mut machine_state = HashMap::new();
	apply_rules(&mut machine_state, &rules);
	let first_a = *machine_state.get("a").unwrap();

	// Run the second part
	let mut machine_state = HashMap::new();
	machine_state.insert(String::from("b"), first_a);
	apply_rules(&mut machine_state, &rules);
	
	return machine_state.get("a").unwrap().to_string();
}