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
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
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 record Instruction(string Op, int Value);
  11. public class GameConsole {
  12. int accumulator = 0;
  13. int pointer = 0;
  14. // Returns a (bool, int) tuple, where the bool indicates whether the program terminated
  15. // normally (if false, it executed an infinite loop), and the int indicates the value of
  16. // the accumulator at the time of termination.
  17. public (bool, int) Execute(List<Instruction> code) {
  18. var instructionsExecuted = new HashSet<int>();
  19. while (pointer < code.Count()) {
  20. if (instructionsExecuted.Contains(pointer)) {
  21. return (false, accumulator); // loop detected
  22. }
  23. instructionsExecuted.Add(pointer);
  24. Instruction instruction = code[pointer];
  25. switch (instruction.Op) {
  26. case "nop":
  27. pointer++;
  28. break;
  29. case "acc":
  30. pointer++;
  31. accumulator += instruction.Value;
  32. break;
  33. case "jmp":
  34. pointer += instruction.Value;
  35. break;
  36. default:
  37. throw new Exception("invalid op");
  38. }
  39. }
  40. return (true, accumulator); // normal termination
  41. }
  42. }
  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 Instruction ParseInstruction(string line) {
  62. string[] tokens = line.Split(' ');
  63. return new Instruction(tokens[0], int.Parse(tokens[1]));
  64. }
  65. static List<Instruction> ParseInstructions(string[] code) {
  66. return code.ToList().Select(ParseInstruction).ToList();
  67. }
  68. static int Part1() {
  69. string[] input = File.ReadAllLines(Util.RootDir + "day08.txt");
  70. List<Instruction> code = ParseInstructions(input);
  71. (bool finished, int result) = new GameConsole().Execute(code);
  72. return result;
  73. }
  74. static int Part2() {
  75. string[] input = File.ReadAllLines(Util.RootDir + "day08.txt");
  76. List<Instruction> code = ParseInstructions(input);
  77. return RepairBrokenInstruction(code);
  78. }
  79. [Fact]
  80. public static void Test() {
  81. string[] example =
  82. @"nop +0
  83. acc +1
  84. jmp +4
  85. acc +3
  86. jmp -3
  87. acc -99
  88. acc +1
  89. jmp -4
  90. acc +6".Split('\n');
  91. List<Instruction> parsed = ParseInstructions(example);
  92. Assert.Equal(9, parsed.Count());
  93. Assert.Equal((false, 5), new GameConsole().Execute(parsed));
  94. Assert.Equal(1610, Part1());
  95. string[] example2 =
  96. @"nop +0
  97. acc +1
  98. jmp +4
  99. acc +3
  100. jmp -3
  101. acc -99
  102. acc +1
  103. nop -4
  104. acc +6".Split('\n');
  105. List<Instruction> parsed2 = ParseInstructions(example2);
  106. Assert.Equal((true, 8), new GameConsole().Execute(parsed2));
  107. Assert.Equal(1703, Part2());
  108. }
  109. }
  110. }