using System.Numerics; var exampleData = new ProblemData("example-input.txt"); var puzzleData = new ProblemData("puzzle-input.txt"); Console.WriteLine("!! === Part01 === !!"); Console.WriteLine("Example root yells: {0}", exampleData.GetMonkeyValuePart01("root")); Console.WriteLine("Puzzle root yells: {0}", puzzleData.GetMonkeyValuePart01("root")); Console.WriteLine("!! === Part02 === !!"); Console.WriteLine("Example human yells: {0}", exampleData.GetMonkeyValuePart02()); Console.WriteLine("Puzzle human yells: {0}", puzzleData.GetMonkeyValuePart02()); abstract class MonkeyCommand { public MonkeyCommand(string name) { Name = name; } public abstract BigInteger GetValue(); public string Name { get; protected set; } public static bool DebugPrint { get; set; } = false; } class MonkeyYellCommand : MonkeyCommand { public MonkeyYellCommand(string name, BigInteger val) : base(name) { Value = val; } public BigInteger Value { get; set; } public override BigInteger GetValue() { return Value; } } class MonkeyMathCommand : MonkeyCommand { public MonkeyMathCommand(string name, Dictionary lookupTable, string left, string right, char op) : base(name) { LookupTable = lookupTable; Left = left; Right = right; if ((op != '-') && (op != '+') && (op != '*') && (op != '/') && (op != '=')) throw new InvalidDataException(); Op = op; } public string Left { get; set; } public string Right { get; set; } public BigInteger RightValue { get { return LookupTable[Right].GetValue(); } } public BigInteger LeftValue { get { return LookupTable[Left].GetValue(); } } public char Op { get; set; } public char InverseOp { get { switch (Op) { case '+': return '-'; case '-': return '+'; case '*': return '/'; case '/': return '*'; default: throw new InvalidDataException(); } } } public Dictionary LookupTable { get; set; } public override BigInteger GetValue() { BigInteger left = LeftValue; BigInteger right = RightValue; switch (Op) { case '+': return left + right; case '-': return left - right; case '*': return left * right; case '/': return left / right; case '=': if (MonkeyCommand.DebugPrint && LookupTable["humn"].GetValue()%10000 == 0) Console.WriteLine("Comparing {0} v {1} with humn {2}", left, right, LookupTable["humn"].GetValue()); return left - right; default: throw new InvalidDataException(); } } } class ProblemData { private Dictionary CommandLookupPart01 { get; set; } private Dictionary CommandLookupPart02 { get; set; } public ProblemData(string filename) { CommandLookupPart01 = new Dictionary(); CommandLookupPart02 = new Dictionary(); using (StreamReader reader = System.IO.File.OpenText(filename)) { while (!reader.EndOfStream) { var line = reader.ReadLine(); if (line == null) throw new InvalidDataException(); var split = line.Split(':', StringSplitOptions.TrimEntries); var name = split[0]; MonkeyCommand monkey, monkey2; // First see if its a value being yelled by the goddamned monkeys int val; if (int.TryParse(split[1], out val)) { // It's a value monkey monkey = new MonkeyYellCommand(name, val); monkey2 = new MonkeyYellCommand(name, val); } else { // It's a math monkey var mathline = split[1].Split(' ', StringSplitOptions.RemoveEmptyEntries); monkey = new MonkeyMathCommand(name, CommandLookupPart01, mathline[0], mathline[2], mathline[1][0]); char op = mathline[1][0]; if (name == "root") op = '='; monkey2 = new MonkeyMathCommand(name, CommandLookupPart02, mathline[0], mathline[2], op); } CommandLookupPart01[name] = monkey; CommandLookupPart02[name] = monkey2; } } } public BigInteger GetMonkeyValuePart01(string name) { return CommandLookupPart01[name].GetValue(); } public BigInteger GetMonkeyValuePart02() { MonkeyMathCommand rootCmd = (MonkeyMathCommand)CommandLookupPart02["root"]; // Find the side the human is on bool isHumanOnLeft = IsHumanOnLeft(CommandLookupPart02, rootCmd); BigInteger targetValue; if (isHumanOnLeft) { targetValue = rootCmd.RightValue; } else { targetValue = rootCmd.LeftValue; } var humnCmd = (MonkeyYellCommand)CommandLookupPart02["humn"]; BigInteger result = 0; BigInteger high = long.MaxValue, low = long.MinValue; BigInteger mid = low + (high - low) / 2; bool incrementUp = false; humnCmd.Value = 1; var first = isHumanOnLeft ? rootCmd.LeftValue : rootCmd.RightValue; humnCmd.Value = 100; var second = isHumanOnLeft ? rootCmd.LeftValue : rootCmd.RightValue; if (second > first) incrementUp = true; while ((isHumanOnLeft ? rootCmd.LeftValue : rootCmd.RightValue) != targetValue) { humnCmd.Value = mid; var newVal = isHumanOnLeft ? rootCmd.LeftValue : rootCmd.RightValue; if ((incrementUp && (newVal > targetValue)) || (!incrementUp && (newVal < targetValue))) { high = low + (high - low) / 2; } else { low = low + (high - low) / 2; } mid = low + (high - low) / 2; } return CommandLookupPart02["humn"].GetValue(); } private static bool IsHumanOnLeft(Dictionary dict, MonkeyMathCommand command) { MonkeyYellCommand humnCmd = (MonkeyYellCommand)dict["humn"]; var ogRight = command.RightValue; var ogLeft = command.LeftValue; humnCmd.Value += 10; var newRight = command.RightValue; var newLeft = command.LeftValue; humnCmd.Value -= 10; if (ogLeft == newLeft) return false; else if (ogRight == newRight) return true; else throw new InvalidDataException(); } }