adventofcode/2020/Day08.cs
2020-12-08 15:23:20 -05:00

123 lines
3.5 KiB
C#

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 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<Instruction> code) {
var instructionsExecuted = new HashSet<int>();
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 record Instruction(string Op, int Value);
public static int RepairBrokenInstruction(List<Instruction> code) {
for (int i = 0; i < code.Count(); i++) {
if (code[i].Op == "acc") {
continue; // this line can't be the broken one
}
List<Instruction> repaired = new List<Instruction>(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 List<Instruction> ParseInstructions(string[] code) {
var result = new List<Instruction>();
foreach (string line in code) {
string[] tokens = line.Split(' ');
result.Add(new Instruction(tokens[0], int.Parse(tokens[1])));
}
return result;
}
static int Part1() {
string[] input = File.ReadAllLines(Util.RootDir + "day08.txt");
List<Instruction> 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<Instruction> 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<Instruction> 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<Instruction> parsed2 = ParseInstructions(example2);
Assert.Equal((true, 8), new GameConsole().Execute(parsed2));
Assert.Equal(1703, Part2());
}
}
}