about summary refs log blame commit diff
path: root/lib/IntCodeVM.cs
blob: b42a90782b52a9169bc4f5b6f524c6d2d9daf0ba (plain) (tree)
1
2
3
4
5
6
7
8

                                 





                          




                                         
 
                                     

                  
                        

                                                                   

                                       







                            


                           

                             



                           
                                               
 
                                      
         
                                                           
         

                                                  
         

















                                                        
             





                                                                       
 










                                                                            
 










                                                                
                 

                                                
                                      

                                                
                                      
                           

                                                    
                                                
                                      

                                               
                                      

                                                         
                              

                                                         
                              

                                                        
                                      

                                                         
                                      



                                          
                                                  

                                                                       






                                      
using System;
using System.Collections.Generic;
using System.Linq;

namespace aoc2019.lib
{
    public class IntCodeVM
    {
        private long i;
        private long relbase;
        public long[] memory;
        private readonly long[] program;
        public Queue<long> input, output;

        public IntCodeVM(string tape)
        {
            i = 0;
            relbase = 0;
            program = tape.Split(',').Select(long.Parse).ToArray();
            memory = program;
            input = new Queue<long>();
            output = new Queue<long>();
        }

        public enum HaltType
        {
            Terminate,
            Waiting
        }

        public void Reset()
        {
            i = 0;
            relbase = 0;
            memory = program;
            input.Clear();
            output.Clear();
        }

        public long Result => output.Dequeue();

        private long MemGet(long addr)
        {
            return addr < memory.Length ? memory[addr] : 0;
        }

        private void MemSet(long addr, long value)
        {
            if (addr < 0) addr = 0;
            if (addr >= memory.Length)
                Array.Resize(ref memory, (int)addr + 1);
            memory[addr] = value;
        }

        private long Mode(long idx)
        {
            var mode = MemGet(i) / 100;
            for (var s = 1; s < idx; s++)
                mode /= 10;
            return mode % 10;
        }

        private long Get(long idx)
        {
            var param = MemGet(i + idx);
            switch (Mode(idx))
            {
                case 0: return MemGet(param);
                case 1: return param;
                case 2: return MemGet(relbase + param);
                default: throw new Exception("invalid parameter mode");
            }
        }

        private void Set(long idx, long val)
        {
            var param = MemGet(i + idx);
            switch (Mode(idx))
            {
                case 0: MemSet(param, val); break;
                case 1: throw new Exception("cannot set in immediate mode");
                case 2: MemSet(relbase + param, val); break;
                default: throw new Exception("invalid parameter mode");
            }
        }

        public HaltType Run(params long[] additionalInput)
        {
            foreach (var s in additionalInput) input.Enqueue(s);
            return Run();
        }
        public HaltType Run()
        {
            while (i < memory.Length)
            {
                var op = MemGet(i) % 100;
                switch (op)
                {
                    case 1:
                        Set(3, Get(1) + Get(2));
                        i += 4; break;
                    case 2:
                        Set(3, Get(1) * Get(2));
                        i += 4; break;
                    case 3:
                        if (!input.Any())
                            return HaltType.Waiting;
                        Set(1, input.Dequeue());
                        i += 2; break;
                    case 4:
                        output.Enqueue(Get(1));
                        i += 2; break;
                    case 5:
                        i = Get(1) == 0 ? i + 3 : Get(2);
                        break;
                    case 6:
                        i = Get(1) != 0 ? i + 3 : Get(2);
                        break;
                    case 7:
                        Set(3, Get(1) < Get(2) ? 1 : 0);
                        i += 4; break;
                    case 8:
                        Set(3, Get(1) == Get(2) ? 1 : 0);
                        i += 4; break;
                    case 9:
                        relbase += Get(1);
                        i += 2; break;
                    case 99:
                        return HaltType.Terminate;
                    default:
                        throw new Exception($"unknown op {op} at {i}");
                }
            }

            return HaltType.Terminate;
        }
    }
}