/// <summary>Runs the IntCode in memory. /// </summary> public void Run() { if (State == IntCodeState.Finished) { throw new ApplicationException("Already finished"); } if (State == IntCodeState.NeedsInput && inputBuffer.Count == 0) { throw new ApplicationException("Needs Input"); } State = IntCodeState.Running; while (State == IntCodeState.Running) { ReadInstruction(); if (!OpCodesDict.ContainsKey(opCode)) { throw new ApplicationException($"Unknown OpCode '{opCode}'."); } var opCodeAction = OpCodesDict[opCode]; opCodeAction.Invoke(this, null); AfterRunStep?.Invoke(this, null); } }
/// <summary> /// If the condition is fulfilled, jumps to a new memory address - if not, proceeds as normal through the code. /// Condition is based on a single parameter /// </summary> public static IntCodeState JumpIf(this IntCodeState state, Func <long, bool> shouldJump) { return(new IntCodeState(state) { State = state.State, Index = shouldJump(state.ReadParameter(1)) ? (int)state.ReadParameter(2) : state.Index + 3 }); }
/// <summary> /// Accepts a single input, applying it to the memory address stored at the single parameter. /// </summary> public static IntCodeState ApplyInput(this IntCodeState state, long input) { return(new IntCodeState(state) { State = state.SetAt(state.WriteParameter(1), input), Index = state.Index + 2 }); }
/// <summary> /// Outputs the value of the single parameter. /// Outputs are not tracked within the intCode state, so a callback is used to apply it as necessary /// </summary> public static IntCodeState ApplyOutput(this IntCodeState state, Action <long> applyOutput) { applyOutput(state.ReadParameter(1)); return(new IntCodeState(state) { State = state.State, Index = state.Index + 2 }); }
public static IntCodeState AdjustRelativeBase(this IntCodeState state) { var baseChange = state.ReadParameter(1); return(new IntCodeState(state) { Index = state.Index + 2, RelativeBase = state.RelativeBase + baseChange }); }
/// <summary> /// Compares the first two values using the given comparator. /// Sets the value in the index given by param 3 to either 1 or 0, reflecting the result of the comparison /// </summary> public static IntCodeState Compare(this IntCodeState state, Func <long, long, bool> comparator) { var valueToStore = comparator(state.ReadParameter(1), state.ReadParameter(2)) ? 1 : 0; return(new IntCodeState(state) { State = state.SetAt(state.WriteParameter(3), valueToStore), Index = state.Index + 4 }); }
/// <summary> /// Applies an arbitrary operation to the first *TWO* parameters, outputting based on the third parameter. /// Will need extending if it needs to be applied to more than two parameters. /// </summary> public static IntCodeState ApplyOperation(this IntCodeState state, Func <long, long, long> operation) { var operationResult = operation(state.ReadParameter(1), state.ReadParameter(2)); return(new IntCodeState(state) { State = state.SetAt(state.WriteParameter(3), operationResult), Index = state.Index + 4 }); }
void OP_Input() { var check = inputBuffer.TryDequeue(out long value); if (!check) { State = IntCodeState.NeedsInput; return; } WriteMemory(AddressPointer + 1, value, GetParameterMode(1)); MoveToNext(2); }
public static IEnumerator <IntCodeOutput> ParseAndRunIntCode( string intCode, Queue <long> inputs = null, long?noun = null, long?verb = null, Func <long> getInput = null) { long GetNextInput() { if (inputs.Any()) { return(inputs.Dequeue()); } if (getInput != null) { return(getInput()); } throw new Exception("Cannot fetch new input - queue empty and no input provider present."); } inputs ??= new Queue <long>(); var initialState = Parser.ParseIntCode(intCode); var state = new IntCodeState { State = initialState.ApplyNounAndVerb(noun, verb), Index = 0 }; var outputs = new List <long>(); while (state.OpCode != 99) { switch (state.OpCode) { // ADD case 1: state = state.ApplyOperation((x, y) => x + y); break; // MULTIPLY case 2: state = state.ApplyOperation((x, y) => x * y); break; // INPUT case 3: state = state.ApplyInput(GetNextInput()); break; // OUTPUT case 4: state = state.ApplyOutput(x => { outputs.Add(x); }); yield return(new IntCodeOutput { Output = outputs.Last(), IsComplete = false, CurrentState = state.State }); break; // JUMP IF TRUE case 5: state = state.JumpIf(x => x != 0); break; // JUMP IF FALSE case 6: state = state.JumpIf(x => x == 0); break; // LESS THAN case 7: state = state.Compare((x, y) => x < y); break; // EQUALS case 8: state = state.Compare((x, y) => x == y); break; // EQUALS case 9: state = state.AdjustRelativeBase(); break; default: throw new Exception($"Unrecognised instruction: {state.Value}"); } } yield return(new IntCodeOutput { IsComplete = true, CurrentState = state.State }); }
void OP_Halt() { State = IntCodeState.Finished; MoveToNext(1); }