diff --git a/2020/CSharp/Day08/Program.cs b/2020/CSharp/Day08/Program.cs new file mode 100644 index 0000000..f3fcd5d --- /dev/null +++ b/2020/CSharp/Day08/Program.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; + +namespace AoCDay08 +{ + class Program + { + + private static readonly Func endCondition = (_o, _p, visits, _ptr, _acc) => visits != 0; + private static readonly string path = "D:\\.projects\\VisualStudio\\repos\\AoCDay08\\AoCDay08\\input.txt"; + + static void Main(string[] args) + { + Console.WriteLine(part1()); + Console.WriteLine(part2()); + } + + static int part1() + { + var program = CodeParser.Parse(path); + var machine = new ExecMachine(program, endCondition); + machine.run(); + return machine.Accumulator; + } + + static (int, int) part2() + { + var program = CodeParser.Parse(path); + var machine = new ExecMachine(new List(), endCondition); + + for (int i=0; i < program.Count; i++) + { + var cloned = program.Select(i => new Instruction(i.opCode, i.param)).ToList(); + + // Swap single instruction + if (cloned[i].opCode == OpCode.JMP) cloned[i].opCode = OpCode.NOP; + else if (cloned[i].opCode == OpCode.NOP) cloned[i].opCode = OpCode.JMP; + + machine.reset(cloned); + machine.run(); + if (machine.ReachedEnd) return (i, machine.Accumulator); + } + return (-1, 0); + } + } + + class ExecMachine + { + private List program; + private Func endCondition; // opCode, param, visits, ptr, acc + + public int Pointer + { + get; private set; + } + public int Accumulator + { + get; private set; + } + public bool ReachedEnd + { + get; private set; + } + + public ExecMachine( + List program, + Func endCondition, + int pointer = 0, + int accumulator = 0 + ) + { + this.program = program; + this.endCondition = endCondition; + Pointer = pointer; + Accumulator = accumulator; + ReachedEnd = false; + } + + public void run() + { + while (true) + { + if (Pointer >= program.Count) + { + ReachedEnd = true; + return; + } + else + { + var opCode = program[Pointer].opCode; + var param = program[Pointer].param; + var visits = program[Pointer].visits; + + if (endCondition(opCode, param, visits, Pointer, Accumulator)) return; + + program[Pointer].visits = visits + 1; + execute(opCode, param); + } + } + } + + private void execute(OpCode opCode, int param) + { + switch (opCode) + { + case OpCode.ACC: + Accumulator += param; + Pointer += 1; + break; + case OpCode.JMP: + Pointer += param; + break; + case OpCode.NOP: + Pointer += 1; + break; + } + } + + public void reset(List program) + { + this.program = program; + Pointer = 0; + Accumulator = 0; + ReachedEnd = false; + } + } + + static class CodeParser + { + public static List Parse(string path) + { + var output = new List(); + try + { + using var stream = new StreamReader(path); + string line; + while ((line = stream.ReadLine()) != null) + { + var instruction = Regex.Match(line, "([a-z]+) ([-+].+)").Groups; + OpCode opCode; + switch (instruction[1].Value) + { + case "acc": + opCode = OpCode.ACC; + break; + case "jmp": + opCode = OpCode.JMP; + break; + case "nop": + opCode = OpCode.NOP; + break; + default: + throw new Exception($"Invalid OpCode <{instruction[1].Value}>"); + } + int param = int.Parse(instruction[2].Value); + output.Add(new Instruction(opCode, param)); + } + } + catch (Exception e) + { + Console.Error.WriteLine(e); + } + return output; + } + } + + class Instruction + { + public OpCode opCode; + public int param; + public int visits; + + public Instruction(OpCode opCode, int param) + { + this.opCode = opCode; + this.param = param; + visits = 0; + } + } + + enum OpCode + { + ACC, JMP, NOP + } +}