2020aoc/Day8.cs

163 lines
6.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
namespace AoC {
internal class Day8 {
private enum Instruction {
ACC, JMP, NOP
}
private record Command(Instruction ins, int value);
private ImmutableDictionary<string, Instruction> InstructionFromString =
new Dictionary<string, Instruction> {
{ "acc", Instruction.ACC },
{ "jmp", Instruction.JMP },
{ "nop", Instruction.NOP }
}.ToImmutableDictionary();
private Regex rx = new(@"^(?<ins>\w+) (?<value>(\+|\-)\d+)");
private bool DoesItLoop(List<Command> commands, int index, ref int accumulator) {
ISet<int> commandIndices = new HashSet<int>();
while (index >= 0 && index < commands.Count && !commandIndices.Contains(index)) {
commandIndices.Add(index);
var command = commands[index];
(accumulator, index) = command.ins switch {
Instruction.ACC => (accumulator + command.value, index + 1),
Instruction.JMP => (accumulator, index + command.value),
Instruction.NOP => (accumulator, index + 1),
_ => throw new ArgumentException("Du doofer Hund!")
};
}
return commandIndices.Contains(index);
}
internal void Solve() {
var readLines = File.ReadAllText("day8/input").Split("\n", StringSplitOptions.RemoveEmptyEntries);
Command LineToCommandSelector(string line) {
var groups = rx.Match(line).Groups;
return new Command(InstructionFromString[groups["ins"].Value], int.Parse(groups["value"].Value));
}
var commands = readLines.Select(LineToCommandSelector).ToList();
SortedSet<int> commandIndices = new SortedSet<int>();
int index = 0;
int accumulator = 0;
while (!commandIndices.Contains(index)) {
commandIndices.Add(index);
var command = commands[index];
(accumulator, index) = command.ins switch {
Instruction.ACC => (accumulator + command.value, index + 1),
Instruction.JMP => (accumulator, index + command.value),
Instruction.NOP => (accumulator, index + 1),
_ => throw new ArgumentException("Du doofer Hund!")
};
}
Console.WriteLine(accumulator);
foreach (var commandIndex in commandIndices.Reverse()) {
index = 0;
accumulator = 0;
if (commands[commandIndex].ins == Instruction.ACC) {
continue;
}
if (commands[commandIndex].ins == Instruction.JMP) {
commands[commandIndex] = new Command(Instruction.NOP, commands[commandIndex].value);
if (!DoesItLoop(commands, index, ref accumulator)) {
break;
}
commands[commandIndex] = new Command(Instruction.JMP, commands[commandIndex].value);
} else {
commands[commandIndex] = new Command(Instruction.JMP, commands[commandIndex].value);
if (!DoesItLoop(commands, index, ref accumulator)) {
break;
}
commands[commandIndex] = new Command(Instruction.NOP, commands[commandIndex].value);
}
}
Console.WriteLine(accumulator);
}
private record Info(int index, int accumulator);
private static void ExecuteInstruction(List<Command> commands, ref int index, ref int accumulator) {
var command = commands[index];
(accumulator, index) = command.ins switch {
Instruction.ACC => (accumulator + command.value, index + 1),
Instruction.JMP => (accumulator, index + command.value),
Instruction.NOP => (accumulator, index + 1),
_ => throw new ArgumentException("Du doofer Hund!")
};
}
private bool DoesItLoop(List<Command> commands, int index, ref int accumulator, ISet<int> visitedInstructions) {
while (index >= 0 && index < commands.Count && !visitedInstructions.Contains(index)) {
visitedInstructions.Add(index);
ExecuteInstruction(commands, ref index, ref accumulator);
}
return visitedInstructions.Contains(index);
}
internal void SolveOn() {
var readLines = File.ReadAllText("day8/input").Split("\n", StringSplitOptions.RemoveEmptyEntries);
Command LineToCommandSelector(string line) {
var groups = rx.Match(line).Groups;
return new Command(InstructionFromString[groups["ins"].Value], int.Parse(groups["value"].Value));
}
var commands = readLines.Select(LineToCommandSelector).ToList();
ISet<int> commandIndices = new HashSet<int>();
Stack<Info> commandInfo = new Stack<Info>();
int index = 0;
int accumulator = 0;
while (!commandIndices.Contains(index)) {
commandIndices.Add(index);
commandInfo.Push(new Info(index, accumulator));
ExecuteInstruction(commands, ref index, ref accumulator);
}
Console.WriteLine(accumulator);
Instruction switchInstruction(Instruction ins) => ins == Instruction.NOP ? Instruction.JMP : Instruction.NOP;
int tempAccumulator = -1;
while (commandInfo.Count > 0) {
var current = commandInfo.Pop();
if (commands[current.index].ins == Instruction.ACC) {
continue;
}
commands[current.index] = new Command(switchInstruction(commands[current.index].ins), commands[current.index].value);
tempAccumulator = current.accumulator;
commandIndices.Remove(current.index);
if (!DoesItLoop(commands, current.index, ref tempAccumulator, commandIndices)) {
break;
}
commands[current.index] = new Command(switchInstruction(commands[current.index].ins), commands[current.index].value);
}
Console.WriteLine(tempAccumulator);
}
}
}