You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
3.5 KiB

3 years ago
  1. using System;
  2. using static System.Console;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text.RegularExpressions;
  7. using Xunit;
  8. namespace AdventOfCode {
  9. public class Day08 {
  10. public class GameConsole {
  11. int accumulator = 0;
  12. int pointer = 0;
  13. // Returns a (bool, int) tuple, where the bool indicates whether the program terminated
  14. // normally (if false, it executed an infinite loop), and the int indicates the value of
  15. // the accumulator at the time of termination.
  16. public (bool, int) Execute(List<Instruction> code) {
  17. var instructionsExecuted = new HashSet<int>();
  18. while (pointer < code.Count()) {
  19. if (instructionsExecuted.Contains(pointer)) {
  20. return (false, accumulator); // loop detected
  21. }
  22. instructionsExecuted.Add(pointer);
  23. Instruction instruction = code[pointer];
  24. switch (instruction.Op) {
  25. case "nop":
  26. pointer++;
  27. break;
  28. case "acc":
  29. pointer++;
  30. accumulator += instruction.Value;
  31. break;
  32. case "jmp":
  33. pointer += instruction.Value;
  34. break;
  35. default:
  36. throw new Exception("invalid op");
  37. }
  38. }
  39. return (true, accumulator); // normal termination
  40. }
  41. }
  42. public record Instruction(string Op, int Value);
  43. public static int RepairBrokenInstruction(List<Instruction> code) {
  44. for (int i = 0; i < code.Count(); i++) {
  45. if (code[i].Op == "acc") {
  46. continue; // this line can't be the broken one
  47. }
  48. List<Instruction> repaired = new List<Instruction>(code);
  49. if (repaired[i].Op == "nop") {
  50. repaired[i] = new Instruction("jmp", repaired[i].Value);
  51. } else {
  52. repaired[i] = new Instruction("nop", repaired[i].Value);
  53. }
  54. var (success, result) = new GameConsole().Execute(repaired);
  55. if (success) {
  56. return result;
  57. }
  58. }
  59. throw new Exception("didn't find a valid instruction to repair");
  60. }
  61. static List<Instruction> ParseInstructions(string[] code) {
  62. var result = new List<Instruction>();
  63. foreach (string line in code) {
  64. string[] tokens = line.Split(' ');
  65. result.Add(new Instruction(tokens[0], int.Parse(tokens[1])));
  66. }
  67. return result;
  68. }
  69. static int Part1() {
  70. string[] input = File.ReadAllLines(Util.RootDir + "day08.txt");
  71. List<Instruction> code = ParseInstructions(input);
  72. (bool finished, int result) = new GameConsole().Execute(code);
  73. return result;
  74. }
  75. static int Part2() {
  76. string[] input = File.ReadAllLines(Util.RootDir + "day08.txt");
  77. List<Instruction> code = ParseInstructions(input);
  78. return RepairBrokenInstruction(code);
  79. }
  80. [Fact]
  81. public static void Test() {
  82. string[] example =
  83. @"nop +0
  84. acc +1
  85. jmp +4
  86. acc +3
  87. jmp -3
  88. acc -99
  89. acc +1
  90. jmp -4
  91. acc +6".Split('\n');
  92. List<Instruction> parsed = ParseInstructions(example);
  93. Assert.Equal(9, parsed.Count());
  94. Assert.Equal((false, 5), new GameConsole().Execute(parsed));
  95. Assert.Equal(1610, Part1());
  96. string[] example2 =
  97. @"nop +0
  98. acc +1
  99. jmp +4
  100. acc +3
  101. jmp -3
  102. acc -99
  103. acc +1
  104. nop -4
  105. acc +6".Split('\n');
  106. List<Instruction> parsed2 = ParseInstructions(example2);
  107. Assert.Equal((true, 8), new GameConsole().Execute(parsed2));
  108. Assert.Equal(1703, Part2());
  109. }
  110. }
  111. }