using System; using static System.Console; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using Xunit; namespace AdventOfCode { public class Day08 { public record Instruction(string Op, int Value); public class GameConsole { int accumulator = 0; int pointer = 0; // Returns a (bool, int) tuple, where the bool indicates whether the program terminated // normally (if false, it executed an infinite loop), and the int indicates the value of // the accumulator at the time of termination. public (bool, int) Execute(List code) { var instructionsExecuted = new HashSet(); while (pointer < code.Count()) { if (instructionsExecuted.Contains(pointer)) { return (false, accumulator); // loop detected } instructionsExecuted.Add(pointer); Instruction instruction = code[pointer]; switch (instruction.Op) { case "nop": pointer++; break; case "acc": pointer++; accumulator += instruction.Value; break; case "jmp": pointer += instruction.Value; break; default: throw new Exception("invalid op"); } } return (true, accumulator); // normal termination } } public static int RepairBrokenInstruction(List code) { for (int i = 0; i < code.Count(); i++) { if (code[i].Op == "acc") { continue; // this line can't be the broken one } List repaired = new List(code); if (repaired[i].Op == "nop") { repaired[i] = new Instruction("jmp", repaired[i].Value); } else { repaired[i] = new Instruction("nop", repaired[i].Value); } var (success, result) = new GameConsole().Execute(repaired); if (success) { return result; } } throw new Exception("didn't find a valid instruction to repair"); } static Instruction ParseInstruction(string line) { string[] tokens = line.Split(' '); return new Instruction(tokens[0], int.Parse(tokens[1])); } static List ParseInstructions(string[] code) { return code.ToList().Select(ParseInstruction).ToList(); } static int Part1() { string[] input = File.ReadAllLines(Util.RootDir + "day08.txt"); List code = ParseInstructions(input); (bool finished, int result) = new GameConsole().Execute(code); return result; } static int Part2() { string[] input = File.ReadAllLines(Util.RootDir + "day08.txt"); List code = ParseInstructions(input); return RepairBrokenInstruction(code); } [Fact] public static void Test() { string[] example = @"nop +0 acc +1 jmp +4 acc +3 jmp -3 acc -99 acc +1 jmp -4 acc +6".Split('\n'); List parsed = ParseInstructions(example); Assert.Equal(9, parsed.Count()); Assert.Equal((false, 5), new GameConsole().Execute(parsed)); Assert.Equal(1610, Part1()); string[] example2 = @"nop +0 acc +1 jmp +4 acc +3 jmp -3 acc -99 acc +1 nop -4 acc +6".Split('\n'); List parsed2 = ParseInstructions(example2); Assert.Equal((true, 8), new GameConsole().Execute(parsed2)); Assert.Equal(1703, Part2()); } } }