Category Six


Fork me on GitHub
2019-12-23

Day 23: Category Six

Description:
--- Day 23: Category Six ---

The droids have finished repairing as much of the ship as they can. Their report indicates that this was a Category 6 disaster - not because it was that bad, but because it destroyed the stockpile of Category 6 network cables as well as most of the ship's network infrastructure.

You'll need to rebuild the network from scratch.

The computers on the network are standard Intcode computers that communicate by sending packets to each other. There are 50 of them in total, each running a copy of the same Network Interface Controller (NIC) software (your puzzle input). The computers have network addresses 0 through 49; when each computer boots up, it will request its network address via a single input instruction. Be sure to give each computer a unique network address.

Once a computer has received its network address, it will begin doing work and communicating over the network by sending and receiving packets. All packets contain two values named X and Y. Packets sent to a computer are queued by the recipient and read in the order they are received.

To send a packet to another computer, the NIC will use three output instructions that provide the destination address of the packet followed by its X and Y values. For example, three output instructions that provide the values 10, 20, 30 would send a packet with X=20 and Y=30 to the computer with address 10.

To receive a packet from another computer, the NIC will use an input instruction. If the incoming packet queue is empty, provide -1. Otherwise, provide the X value of the next packet; the computer will then use a second input instruction to receive the Y value for the same packet. Once both values of the packet are read in this way, the packet is removed from the queue.

Note that these input and output instructions never block. Specifically, output instructions do not wait for the sent packet to be received - the computer might send multiple packets before receiving any. Similarly, input instructions do not wait for a packet to arrive - if no packet is waiting, input instructions should receive -1.

Boot up all 50 computers and attach them to your network. What is the Y value of the first packet sent to address 255?

--- Part Two ---

Packets sent to address 255 are handled by a device called a NAT (Not Always Transmitting). The NAT is responsible for managing power consumption of the network by blocking certain packets and watching for idle periods in the computers.

If a packet would be sent to address 255, the NAT receives it instead. The NAT remembers only the last packet it receives; that is, the data in each packet it receives overwrites the NAT's packet memory with the new packet's X and Y values.

The NAT also monitors all computers on the network. If all computers have empty incoming packet queues and are continuously trying to receive packets without sending packets, the network is considered idle.

Once the network is idle, the NAT sends only the last packet it received to address 0; this will cause the computers on the network to resume activity. In this way, the NAT can throttle power consumption of the network when the ship needs power in other areas.

Monitor packets released to the computer at address 0 by the NAT. What is the first Y value delivered by the NAT to the computer at address 0 twice in a row?

Input:
3,62,1001,62,11,10,109,2237,105,1,0,787,571,1791,1059,2169,2138,1601,894,1496,828,1851,1150,1311,1977,993,962,1944,865,1636,1245,1531,709,2008,608,1183,1214,1884,1342,1028,676,2105,1379,1090,2045,925,1280,1729,647,2206,1566,1667,2076,1700,1762,750,1822,1913,1418,1119,1453,0,0,0,0,0,0,0,0,0,0,0,0,3,64,1008,64,-1,62,1006,62,88,1006,61,170,1106,0,73,3,65,21001,64,0,1,20101,0,66,2,21102,1,105,0,1105,1,436,1201,1,-1,64,1007,64,0,62,1005,62,73,7,64,67,62,1006,62,73,1002,64,2,132,1,132,68,132,1001,0,0,62,1001,132,1,140,8,0,65,63,2,63,62,62,1005,62,73,1002,64,2,161,1,161,68,161,1102,1,1,0,1001,161,1,169,101,0,65,0,1101,1,0,61,1101,0,0,63,7,63,67,62,1006,62,203,1002,63,2,194,1,68,194,194,1006,0,73,1001,63,1,63,1106,0,178,21102,210,1,0,106,0,69,1202,1,1,70,1102,0,1,63,7,63,71,62,1006,62,250,1002,63,2,234,1,72,234,234,4,0,101,1,234,240,4,0,4,70,1001,63,1,63,1105,1,218,1106,0,73,109,4,21101,0,0,-3,21101,0,0,-2,20207,-2,67,-1,1206,-1,293,1202,-2,2,283,101,1,283,283,1,68,283,283,22001,0,-3,-3,21201,-2,1,-2,1105,1,263,21201,-3,0,-3,109,-4,2105,1,0,109,4,21102,1,1,-3,21101,0,0,-2,20207,-2,67,-1,1206,-1,342,1202,-2,2,332,101,1,332,332,1,68,332,332,22002,0,-3,-3,21201,-2,1,-2,1105,1,312,21201,-3,0,-3,109,-4,2106,0,0,109,1,101,1,68,358,21002,0,1,1,101,3,68,366,21002,0,1,2,21102,376,1,0,1106,0,436,22102,1,1,0,109,-1,2105,1,0,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,2147483648,4294967296,8589934592,17179869184,34359738368,68719476736,137438953472,274877906944,549755813888,1099511627776,2199023255552,4398046511104,8796093022208,17592186044416,35184372088832,70368744177664,140737488355328,281474976710656,562949953421312,1125899906842624,109,8,21202,-6,10,-5,22207,-7,-5,-5,1205,-5,521,21101,0,0,-4,21101,0,0,-3,21101,51,0,-2,21201,-2,-1,-2,1201,-2,385,471,20102,1,0,-1,21202,-3,2,-3,22207,-7,-1,-5,1205,-5,496,21201,-3,1,-3,22102,-1,-1,-5,22201,-7,-5,-7,22207,-3,-6,-5,1205,-5,515,22102,-1,-6,-5,22201,-3,-5,-3,22201,-1,-4,-4,1205,-2,461,1106,0,547,21102,-1,1,-4,21202,-6,-1,-6,21207,-7,0,-5,1205,-5,547,22201,-7,-6,-7,21201,-4,1,-4,1106,0,529,22101,0,-4,-7,109,-8,2106,0,0,109,1,101,1,68,563,21002,0,1,0,109,-1,2105,1,0,1101,0,99859,66,1101,4,0,67,1102,1,598,68,1101,0,302,69,1101,0,1,71,1102,606,1,72,1105,1,73,0,0,0,0,0,0,0,0,9,110373,1101,0,97157,66,1102,1,1,67,1101,635,0,68,1101,556,0,69,1101,0,5,71,1102,1,637,72,1106,0,73,1,1,34,47954,39,170902,8,192586,44,86601,14,52963,1101,0,103183,66,1101,1,0,67,1101,674,0,68,1101,0,556,69,1102,0,1,71,1101,0,676,72,1105,1,73,1,1408,1102,51199,1,66,1101,2,0,67,1101,0,703,68,1101,0,302,69,1101,1,0,71,1101,707,0,72,1106,0,73,0,0,0,0,1,99859,1101,71353,0,66,1102,6,1,67,1101,0,736,68,1102,1,302,69,1102,1,1,71,1102,1,748,72,1106,0,73,0,0,0,0,0,0,0,0,0,0,0,0,16,107722,1102,1,28867,66,1101,0,4,67,1102,1,777,68,1101,0,302,69,1101,0,1,71,1102,785,1,72,1106,0,73,0,0,0,0,0,0,0,0,10,35671,1101,14653,0,66,1102,1,1,67,1102,814,1,68,1102,556,1,69,1102,6,1,71,1101,816,0,72,1105,1,73,1,29058,10,71342,47,166142,47,249213,20,36373,20,72746,20,109119,1101,0,36791,66,1101,0,4,67,1101,855,0,68,1101,253,0,69,1101,1,0,71,1101,863,0,72,1106,0,73,0,0,0,0,0,0,0,0,16,53861,1102,1,28387,66,1101,1,0,67,1101,892,0,68,1101,556,0,69,1101,0,0,71,1102,894,1,72,1105,1,73,1,1450,1102,1,95467,66,1102,1,1,67,1102,1,921,68,1101,556,0,69,1102,1,1,71,1102,923,1,72,1105,1,73,1,-671,8,96293,1101,0,23977,66,1101,0,4,67,1101,0,952,68,1101,0,302,69,1102,1,1,71,1102,960,1,72,1105,1,73,0,0,0,0,0,0,0,0,31,8324,1102,1,82493,66,1102,1,1,67,1101,0,989,68,1102,1,556,69,1102,1,1,71,1102,991,1,72,1106,0,73,1,125,4,164319,1101,0,52963,66,1101,0,3,67,1101,1020,0,68,1101,0,302,69,1101,1,0,71,1102,1026,1,72,1105,1,73,0,0,0,0,0,0,47,83071,1101,0,49597,66,1102,1,1,67,1102,1,1055,68,1102,1,556,69,1102,1,1,71,1101,0,1057,72,1105,1,73,1,7890373,30,93118,1102,6569,1,66,1101,0,1,67,1101,1086,0,68,1102,1,556,69,1102,1,1,71,1101,0,1088,72,1106,0,73,1,-31,1,299577,1101,0,3083,66,1101,1,0,67,1101,0,1117,68,1101,556,0,69,1101,0,0,71,1101,1119,0,72,1106,0,73,1,1970,1102,11867,1,66,1102,1,1,67,1102,1146,1,68,1102,556,1,69,1101,1,0,71,1101,1148,0,72,1106,0,73,1,14447,39,256353,1101,0,103567,66,1101,0,2,67,1101,0,1177,68,1102,302,1,69,1101,1,0,71,1102,1,1181,72,1105,1,73,0,0,0,0,29,51199,1102,1,7193,66,1102,1,1,67,1101,1210,0,68,1101,0,556,69,1102,1,1,71,1101,0,1212,72,1105,1,73,1,1061,8,288879,1102,1,90469,66,1101,1,0,67,1101,1241,0,68,1102,1,556,69,1102,1,1,71,1102,1243,1,72,1105,1,73,1,-186,14,158889,1102,1,41851,66,1102,1,1,67,1101,0,1272,68,1102,1,556,69,1101,3,0,71,1101,0,1274,72,1106,0,73,1,5,4,109546,4,219092,21,142706,1101,75767,0,66,1102,1,1,67,1101,1307,0,68,1101,0,556,69,1102,1,1,71,1102,1,1309,72,1105,1,73,1,9839,6,17989,1101,0,32089,66,1101,0,1,67,1101,1338,0,68,1102,556,1,69,1102,1,1,71,1101,0,1340,72,1106,0,73,1,1217,34,23977,1102,1,24889,66,1101,0,4,67,1102,1,1369,68,1101,0,302,69,1101,1,0,71,1102,1,1377,72,1106,0,73,0,0,0,0,0,0,0,0,31,10405,1102,1,2081,66,1102,1,5,67,1102,1406,1,68,1101,0,253,69,1101,0,1,71,1101,1416,0,72,1106,0,73,0,0,0,0,0,0,0,0,0,0,11,103567,1101,0,83071,66,1101,0,3,67,1102,1445,1,68,1101,0,302,69,1102,1,1,71,1101,1451,0,72,1106,0,73,0,0,0,0,0,0,9,36791,1101,25073,0,66,1101,1,0,67,1101,1480,0,68,1102,1,556,69,1102,7,1,71,1101,0,1482,72,1105,1,73,1,2,11,207134,29,102398,1,199718,6,35978,6,53967,21,71353,21,214059,1101,0,96293,66,1102,3,1,67,1102,1,1523,68,1102,1,302,69,1102,1,1,71,1102,1,1529,72,1106,0,73,0,0,0,0,0,0,31,2081,1102,36373,1,66,1101,0,3,67,1102,1558,1,68,1102,1,302,69,1101,1,0,71,1101,1564,0,72,1106,0,73,0,0,0,0,0,0,9,73582,1101,85451,0,66,1101,3,0,67,1101,1593,0,68,1102,1,302,69,1102,1,1,71,1101,0,1599,72,1105,1,73,0,0,0,0,0,0,31,6243,1101,0,17989,66,1101,0,3,67,1102,1628,1,68,1101,302,0,69,1102,1,1,71,1101,1634,0,72,1106,0,73,0,0,0,0,0,0,44,28867,1101,50789,0,66,1101,1,0,67,1102,1663,1,68,1102,1,556,69,1101,1,0,71,1101,1665,0,72,1105,1,73,1,2447,44,57734,1102,1,71257,66,1102,1,1,67,1102,1,1694,68,1101,556,0,69,1102,2,1,71,1102,1696,1,72,1105,1,73,1,313,1,399436,14,105926,1102,102497,1,66,1102,1,1,67,1102,1,1727,68,1102,556,1,69,1101,0,0,71,1101,1729,0,72,1106,0,73,1,1642,1102,73259,1,66,1101,1,0,67,1102,1,1756,68,1101,556,0,69,1102,1,2,71,1102,1,1758,72,1106,0,73,1,10,4,54773,21,428118,1102,1,20477,66,1102,1,1,67,1102,1,1789,68,1101,0,556,69,1102,1,0,71,1101,1791,0,72,1106,0,73,1,1402,1101,0,92761,66,1102,1,1,67,1101,1818,0,68,1102,1,556,69,1101,0,1,71,1101,0,1820,72,1106,0,73,1,-173,39,85451,1102,70969,1,66,1101,1,0,67,1101,1849,0,68,1101,0,556,69,1101,0,0,71,1101,0,1851,72,1105,1,73,1,1606,1101,0,35671,66,1101,2,0,67,1102,1878,1,68,1101,0,302,69,1101,0,1,71,1102,1882,1,72,1105,1,73,0,0,0,0,9,147164,1102,83077,1,66,1101,1,0,67,1101,1911,0,68,1102,1,556,69,1102,1,0,71,1101,0,1913,72,1105,1,73,1,1368,1102,9973,1,66,1102,1,1,67,1101,0,1940,68,1101,556,0,69,1102,1,1,71,1101,1942,0,72,1105,1,73,1,257,27,99556,1101,0,53861,66,1102,2,1,67,1102,1,1971,68,1101,351,0,69,1102,1,1,71,1102,1,1975,72,1105,1,73,0,0,0,0,255,14653,1101,0,31159,66,1101,0,1,67,1102,1,2004,68,1101,556,0,69,1102,1,1,71,1101,0,2006,72,1106,0,73,1,160,21,356765,1102,31667,1,66,1102,1,1,67,1101,2035,0,68,1101,556,0,69,1101,0,4,71,1102,1,2037,72,1106,0,73,1,3,34,95908,27,24889,27,74667,30,46559,1102,80387,1,66,1101,1,0,67,1102,1,2072,68,1102,1,556,69,1102,1,1,71,1102,2074,1,72,1106,0,73,1,-1863,27,49778,1101,0,81281,66,1102,1,1,67,1102,1,2103,68,1101,0,556,69,1101,0,0,71,1102,1,2105,72,1105,1,73,1,1029,1101,0,46559,66,1102,2,1,67,1101,0,2132,68,1101,0,302,69,1101,1,0,71,1101,0,2136,72,1105,1,73,0,0,0,0,31,4162,1101,0,68281,66,1102,1,1,67,1101,0,2165,68,1101,556,0,69,1102,1,1,71,1102,1,2167,72,1105,1,73,1,821,34,71931,1102,54773,1,66,1101,0,4,67,1102,2196,1,68,1102,1,302,69,1102,1,1,71,1102,1,2204,72,1106,0,73,0,0,0,0,0,0,0,0,21,285412,1101,0,33161,66,1102,1,1,67,1102,2233,1,68,1102,1,556,69,1102,1,1,71,1102,2235,1,72,1105,1,73,1,19,44,115468

Part 1:
namespace AdventOfCode2019_23_1
{
	const DAY     = 23;
	const PROBLEM = 1;

	export async function run()
	{
		let input = await AdventOfCode.getInput(DAY);
		if (input == null) return;

		const code = input.trim().split(",").map(p => parseInt(p.trim()));

		let intps = new Array<Interpreter>(50);
		for(let i=0; i<50; i++) intps[i] = new Interpreter(code, [i]);
		for(let i=0; i<50; i++) intps[i].blocking_io = false;

		let counter = new Array<number>(50*50).fill(0);

		const IDS = [
			'0','1','2','3','4','5','6','7','8','9',
			'A','B','C','D','E','F','G','H','I','J',
			'K','L','M','N','O','P','Q','R','S','T',
			'U','V','W','X','Y','Z','a','b','c','d',
			'e','f','g','h','i','j','k','l','m','n',
			'o','p','q','r','s','t','u','v','w','x',
			'y','z'
		];

		for(;;)
		{
			for(let i=0; i<50; i++)
			{
				intps[i].singleStep();
				if (intps[i].output.length === 3)
				{
					let d = intps[i].output[0];
					let x = intps[i].output[1];
					let y = intps[i].output[2];

					intps[i].output = [];

					if (d === 255)
					{
						AdventOfCode.output(DAY, PROBLEM, y.toString());

						return;
					}

					intps[d].inputqueue.push(x);
					intps[d].inputqueue.push(y);

					AdventOfCode.outputConsole(`[${i}]  --[${x}|${y}]-->  [${d}]`)

					counter[i*50+d]++;
				}
			}

			if (AdventOfCode.Config.immediateOutputEnabled)
			{
				let str = "  ";
				for (let dst = 0; dst < 50; dst++) str += IDS[dst] + " ";
				str += "\n";
				for (let src = 0; src < 50; src++)
				{
					str += IDS[src] + " ";
					for (let dst = 0; dst < 50; dst++)
					{
						const v = counter[src*50+dst];

						     if (v    === 0) str += "." + " ";
						else if (v %2 === 1) str += "#" + " ";
						else if (v %2 === 0) str += "X" + " ";
					}

					str += "\n";
				}

				await AdventOfCode.outputIntermed(str);
			} 
		}
	}

	class Interpreter
	{
		program: InfMem;
		inputqueue: number[];
		instructionpointer: number;
		output: number[];
		relative_base: number;
		blocking_io: boolean;

		is_halted: boolean = false;

		constructor(prog: number[], input: number[])
		{
			this.program = new InfMem(prog);
			this.inputqueue = input;
			this.instructionpointer = 0;
			this.output = [];
			this.relative_base = 0;
			this.blocking_io = true;
		}

		fullRun() : number[]
		{
			while(!this.is_halted)
			{
				const r = this.singleStep();

				if (r === StepResult.EXECUTED) continue;
				if (r === StepResult.HALTED) return this.output;
				if (r === StepResult.WAITING_FOR_IN) throw "not enough input";

				throw "unknown output of singleStep";
			}

			return this.output;
		}

		autoRun() : StepResult
		{
			while(!this.is_halted)
			{
				const r = this.singleStep();

				if (r === StepResult.EXECUTED) continue;
				if (r === StepResult.HALTED) return StepResult.HALTED;
				if (r === StepResult.WAITING_FOR_IN) return StepResult.WAITING_FOR_IN;

				throw "unknown output of singleStep";
			}

			return StepResult.HALTED;
		}

		singleStep() : StepResult
		{
			const cmd = new Op(this.program.r(this.instructionpointer));

			if (cmd.opcode == OpCode.ADD)
			{
				const p0 = cmd.getParameter(this, 0);
				const p1 = cmd.getParameter(this, 1);
				const pv = p0 + p1;
				cmd.setParameter(this, 2, pv);

				this.incInstrPtr(cmd);
				
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.MUL)
			{
				const p0 = cmd.getParameter(this, 0);
				const p1 = cmd.getParameter(this, 1);
				const pv = p0 * p1;
				cmd.setParameter(this, 2, pv);

				this.incInstrPtr(cmd);
				
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.HALT)
			{
				this.is_halted = true;
				return StepResult.HALTED;
			}
			else if (cmd.opcode == OpCode.IN)
			{
				if (this.inputqueue.length == 0) 
				{
					if (this.blocking_io) return StepResult.WAITING_FOR_IN;

					cmd.setParameter(this, 0, -1);
					this.incInstrPtr(cmd);
					return StepResult.EXECUTED;
				}

				const pv = this.inputqueue[0];
				cmd.setParameter(this, 0, pv);
				this.inputqueue = this.inputqueue.slice(1);

				this.incInstrPtr(cmd);
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.OUT)
			{
				const p0 = cmd.getParameter(this, 0);
				this.output.push(p0);
				//AdventOfCode.outputConsole("# " + p0);

				this.incInstrPtr(cmd);

				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.TJMP)
			{
				const p0 = cmd.getParameter(this, 0);
				if (p0 != 0) this.instructionpointer = cmd.getParameter(this, 1);
				else this.incInstrPtr(cmd);
				
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.FJMP)
			{
				const p0 = cmd.getParameter(this, 0);
				if (p0 == 0) this.instructionpointer = cmd.getParameter(this, 1);
				else this.incInstrPtr(cmd);
				
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.LT)
			{
				const p0 = cmd.getParameter(this, 0);
				const p1 = cmd.getParameter(this, 1);
				const pv = p0 < p1 ? 1 : 0;
				cmd.setParameter(this, 2, pv);

				this.incInstrPtr(cmd);
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.EQ)
			{
				const p0 = cmd.getParameter(this, 0);
				const p1 = cmd.getParameter(this, 1);
				const pv = p0 == p1 ? 1 : 0;
				cmd.setParameter(this, 2, pv);

				this.incInstrPtr(cmd);
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.ARB)
			{
				const p0 = cmd.getParameter(this, 0);
				this.relative_base = this.relative_base+p0;

				this.incInstrPtr(cmd);
				return StepResult.EXECUTED;
			}
			else throw "Unknown Op: " + cmd.opcode + " @ " + this.instructionpointer;
		}

		private incInstrPtr(cmd: Op)
		{
			this.instructionpointer += 1 + cmd.parametercount;
		}
	}

	enum StepResult { EXECUTED, HALTED, WAITING_FOR_IN }

	enum OpCode 
	{
		ADD  = 1,
		MUL  = 2,
		IN   = 3,
		OUT  = 4,
		TJMP = 5,
		FJMP = 6,
		LT   = 7,
		EQ   = 8,
		ARB  = 9,
		HALT = 99,
	}

	enum ParamMode
	{
		POSITION_MODE  = 0,
		IMMEDIATE_MODE = 1,
		RELATIVE_MODE  = 2,
	}

	class Op
	{
		opcode: OpCode;
		modes: ParamMode[];

		name: string;
		parametercount: number;

		constructor(v: number)
		{
			this.opcode = v%100;
			v = Math.floor(v/100);
			this.modes = [];
			for(let i=0; i<4; i++) 
			{
				this.modes.push(v%10);
				v = Math.floor(v/10);
			}

			     if (this.opcode == OpCode.ADD)  { this.name="ADD";   this.parametercount=3; }
			else if (this.opcode == OpCode.MUL)  { this.name="MUL";   this.parametercount=3; }
			else if (this.opcode == OpCode.HALT) { this.name="HALT";  this.parametercount=0; }
			else if (this.opcode == OpCode.IN)   { this.name="IN";    this.parametercount=1; }
			else if (this.opcode == OpCode.OUT)  { this.name="OUT";   this.parametercount=1; }
			else if (this.opcode == OpCode.TJMP) { this.name="TJMP";  this.parametercount=2; }
			else if (this.opcode == OpCode.FJMP) { this.name="FJMP";  this.parametercount=2; }
			else if (this.opcode == OpCode.LT)   { this.name="LT";    this.parametercount=3; }
			else if (this.opcode == OpCode.EQ)   { this.name="EQ";    this.parametercount=3; }
			else if (this.opcode == OpCode.ARB)  { this.name="ARB";   this.parametercount=1; }
			else throw "Unknown opcode: "+this.opcode;
		}

		getParameter(proc: Interpreter, index: number): number
		{
			const prog = proc.program;
			const ip   = proc.instructionpointer;

			let p = prog.r(ip+1+index);

				 if (this.modes[index] == ParamMode.POSITION_MODE)  p = prog.r(p);
			else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) p = p;
			else if (this.modes[index] == ParamMode.RELATIVE_MODE)  p = prog.r(proc.relative_base+p);
			else throw "Unknown ParamMode: "+this.modes[index];

			return p;
		}

		setParameter(proc: Interpreter, index: number, value: number): void
		{
			const prog = proc.program;
			const ip   = proc.instructionpointer;

			let p = prog.r(ip+1+index);

				 if (this.modes[index] == ParamMode.POSITION_MODE)  prog.w(p, value);
			else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) throw "Immediate mode not allowed in write";
			else if (this.modes[index] == ParamMode.RELATIVE_MODE)  prog.w(proc.relative_base+p, value);
			else throw "Unknown ParamMode: "+this.modes[index];
		}
	}

	class InfMem
	{
		private data: { [_:number]:number } = {};   

		constructor(v: number[])
		{
			for(let i=0; i<v.length;i++) this.data[i]=v[i];
		}

		r(pos: number): number
		{
			if (!(pos in this.data)) this.data[pos] = 0;
			return this.data[pos];
		}

		w(pos: number, val: number): number
		{
			return this.data[pos] = val;
		}
	}
}

Result: 27061

Part 2:
namespace AdventOfCode2019_23_2
{
	const DAY     = 23;
	const PROBLEM = 2;

	export async function run()
	{
		let input = await AdventOfCode.getInput(DAY);
		if (input == null) return;

		const code = input.trim().split(",").map(p => parseInt(p.trim()));

		let intps = new Array<Interpreter>(50);
		for(let i=0; i<50; i++) intps[i] = new Interpreter(code, [i]);
		for(let i=0; i<50; i++) intps[i].blocking_io = false;

		let counter = new Array<number>(50*50).fill(0);

		const IDS = [
			'0','1','2','3','4','5','6','7','8','9',
			'A','B','C','D','E','F','G','H','I','J',
			'K','L','M','N','O','P','Q','R','S','T',
			'U','V','W','X','Y','Z','a','b','c','d',
			'e','f','g','h','i','j','k','l','m','n',
			'o','p','q','r','s','t','u','v','w','x',
			'y','z'
		];

		let last_nat_y: number = -1;

		let NAT: [number, number] = [0, 0];

		for(;;)
		{
			let issend = false;
			for(let i=0; i<50; i++)
			{
				intps[i].singleStep();
				if (intps[i].output.length === 3)
				{
					let d = intps[i].output[0];
					let x = intps[i].output[1];
					let y = intps[i].output[2];

					intps[i].output = [];

					issend = true;

					if (d === 255)
					{
						NAT = [x, y];

						AdventOfCode.outputConsole(`[${i}]  --[${x}|${y}]-->  NAT`);
					}
					else
					{
						intps[d].inputqueue.push(x);
						intps[d].inputqueue.push(y);
	
						AdventOfCode.outputConsole(`[${i}]  --[${x}|${y}]-->  [${d}]`);
	
						counter[i*50+d]++;
					}

				}
			}

			if (issend && AdventOfCode.Config.immediateOutputEnabled)
			{
				let str = "      ";
				for (let dst = 0; dst < 50; dst++) str += IDS[dst % 10] + " ";
				str += "\n";
				for (let src = 0; src < 50; src++)
				{
					str += "[" + (intps[src].last_io_success ? "X":".") + "] " + IDS[src % 10] + " ";
					for (let dst = 0; dst < 50; dst++)
					{
						const v = counter[src*50+dst];

						     if (v    === 0) str += "." + " ";
						else if (v %2 === 1) str += "#" + " ";
						else if (v %2 === 0) str += "X" + " ";
					}

					str += "\n";
				}

				await AdventOfCode.outputIntermed(str);
			}

			if (intps.every(p => !p.last_io_success))
			{
				intps[0].inputqueue.push(NAT[0]);
				intps[0].inputqueue.push(NAT[1]);
				intps[0].last_io_success = true;

				AdventOfCode.outputConsole(`NAT  --[${NAT[0]}|${NAT[1]}]-->  [0]`);

				if (last_nat_y === NAT[1])
				{
					AdventOfCode.output(DAY, PROBLEM, last_nat_y.toString());
					return;
				}

				last_nat_y = NAT[1];
			}
		}

	}

	class Interpreter
	{
		program: InfMem;
		inputqueue: number[];
		instructionpointer: number;
		output: number[];
		relative_base: number;
		blocking_io: boolean;

		is_halted: boolean = false;
		last_io_success: boolean = true;

		constructor(prog: number[], input: number[])
		{
			this.program = new InfMem(prog);
			this.inputqueue = input;
			this.instructionpointer = 0;
			this.output = [];
			this.relative_base = 0;
			this.blocking_io = true;
		}

		fullRun() : number[]
		{
			while(!this.is_halted)
			{
				const r = this.singleStep();

				if (r === StepResult.EXECUTED) continue;
				if (r === StepResult.HALTED) return this.output;
				if (r === StepResult.WAITING_FOR_IN) throw "not enough input";

				throw "unknown output of singleStep";
			}

			return this.output;
		}

		autoRun() : StepResult
		{
			while(!this.is_halted)
			{
				const r = this.singleStep();

				if (r === StepResult.EXECUTED) continue;
				if (r === StepResult.HALTED) return StepResult.HALTED;
				if (r === StepResult.WAITING_FOR_IN) return StepResult.WAITING_FOR_IN;

				throw "unknown output of singleStep";
			}

			return StepResult.HALTED;
		}

		singleStep() : StepResult
		{
			const cmd = new Op(this.program.r(this.instructionpointer));

			if (cmd.opcode == OpCode.ADD)
			{
				const p0 = cmd.getParameter(this, 0);
				const p1 = cmd.getParameter(this, 1);
				const pv = p0 + p1;
				cmd.setParameter(this, 2, pv);

				this.incInstrPtr(cmd);
				
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.MUL)
			{
				const p0 = cmd.getParameter(this, 0);
				const p1 = cmd.getParameter(this, 1);
				const pv = p0 * p1;
				cmd.setParameter(this, 2, pv);

				this.incInstrPtr(cmd);
				
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.HALT)
			{
				this.is_halted = true;
				return StepResult.HALTED;
			}
			else if (cmd.opcode == OpCode.IN)
			{
				if (this.inputqueue.length == 0) 
				{
					if (this.blocking_io) return StepResult.WAITING_FOR_IN;

					cmd.setParameter(this, 0, -1);
					this.incInstrPtr(cmd);
					this.last_io_success = false;
					return StepResult.EXECUTED;
				}

				const pv = this.inputqueue[0];
				cmd.setParameter(this, 0, pv);
				this.inputqueue = this.inputqueue.slice(1);

				this.incInstrPtr(cmd);
				this.last_io_success = true;
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.OUT)
			{
				const p0 = cmd.getParameter(this, 0);
				this.output.push(p0);
				//AdventOfCode.outputConsole("# " + p0);

				this.incInstrPtr(cmd);

				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.TJMP)
			{
				const p0 = cmd.getParameter(this, 0);
				if (p0 != 0) this.instructionpointer = cmd.getParameter(this, 1);
				else this.incInstrPtr(cmd);
				
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.FJMP)
			{
				const p0 = cmd.getParameter(this, 0);
				if (p0 == 0) this.instructionpointer = cmd.getParameter(this, 1);
				else this.incInstrPtr(cmd);
				
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.LT)
			{
				const p0 = cmd.getParameter(this, 0);
				const p1 = cmd.getParameter(this, 1);
				const pv = p0 < p1 ? 1 : 0;
				cmd.setParameter(this, 2, pv);

				this.incInstrPtr(cmd);
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.EQ)
			{
				const p0 = cmd.getParameter(this, 0);
				const p1 = cmd.getParameter(this, 1);
				const pv = p0 == p1 ? 1 : 0;
				cmd.setParameter(this, 2, pv);

				this.incInstrPtr(cmd);
				return StepResult.EXECUTED;
			}
			else if (cmd.opcode == OpCode.ARB)
			{
				const p0 = cmd.getParameter(this, 0);
				this.relative_base = this.relative_base+p0;

				this.incInstrPtr(cmd);
				return StepResult.EXECUTED;
			}
			else throw "Unknown Op: " + cmd.opcode + " @ " + this.instructionpointer;
		}

		private incInstrPtr(cmd: Op)
		{
			this.instructionpointer += 1 + cmd.parametercount;
		}
	}

	enum StepResult { EXECUTED, HALTED, WAITING_FOR_IN }

	enum OpCode 
	{
		ADD  = 1,
		MUL  = 2,
		IN   = 3,
		OUT  = 4,
		TJMP = 5,
		FJMP = 6,
		LT   = 7,
		EQ   = 8,
		ARB  = 9,
		HALT = 99,
	}

	enum ParamMode
	{
		POSITION_MODE  = 0,
		IMMEDIATE_MODE = 1,
		RELATIVE_MODE  = 2,
	}

	class Op
	{
		opcode: OpCode;
		modes: ParamMode[];

		name: string;
		parametercount: number;

		constructor(v: number)
		{
			this.opcode = v%100;
			v = Math.floor(v/100);
			this.modes = [];
			for(let i=0; i<4; i++) 
			{
				this.modes.push(v%10);
				v = Math.floor(v/10);
			}

			     if (this.opcode == OpCode.ADD)  { this.name="ADD";   this.parametercount=3; }
			else if (this.opcode == OpCode.MUL)  { this.name="MUL";   this.parametercount=3; }
			else if (this.opcode == OpCode.HALT) { this.name="HALT";  this.parametercount=0; }
			else if (this.opcode == OpCode.IN)   { this.name="IN";    this.parametercount=1; }
			else if (this.opcode == OpCode.OUT)  { this.name="OUT";   this.parametercount=1; }
			else if (this.opcode == OpCode.TJMP) { this.name="TJMP";  this.parametercount=2; }
			else if (this.opcode == OpCode.FJMP) { this.name="FJMP";  this.parametercount=2; }
			else if (this.opcode == OpCode.LT)   { this.name="LT";    this.parametercount=3; }
			else if (this.opcode == OpCode.EQ)   { this.name="EQ";    this.parametercount=3; }
			else if (this.opcode == OpCode.ARB)  { this.name="ARB";   this.parametercount=1; }
			else throw "Unknown opcode: "+this.opcode;
		}

		getParameter(proc: Interpreter, index: number): number
		{
			const prog = proc.program;
			const ip   = proc.instructionpointer;

			let p = prog.r(ip+1+index);

				 if (this.modes[index] == ParamMode.POSITION_MODE)  p = prog.r(p);
			else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) p = p;
			else if (this.modes[index] == ParamMode.RELATIVE_MODE)  p = prog.r(proc.relative_base+p);
			else throw "Unknown ParamMode: "+this.modes[index];

			return p;
		}

		setParameter(proc: Interpreter, index: number, value: number): void
		{
			const prog = proc.program;
			const ip   = proc.instructionpointer;

			let p = prog.r(ip+1+index);

				 if (this.modes[index] == ParamMode.POSITION_MODE)  prog.w(p, value);
			else if (this.modes[index] == ParamMode.IMMEDIATE_MODE) throw "Immediate mode not allowed in write";
			else if (this.modes[index] == ParamMode.RELATIVE_MODE)  prog.w(proc.relative_base+p, value);
			else throw "Unknown ParamMode: "+this.modes[index];
		}
	}

	class InfMem
	{
		private data: { [_:number]:number } = {};   

		constructor(v: number[])
		{
			for(let i=0; i<v.length;i++) this.data[i]=v[i];
		}

		r(pos: number): number
		{
			if (!(pos in this.data)) this.data[pos] = 0;
			return this.data[pos];
		}

		w(pos: number, val: number): number
		{
			return this.data[pos] = val;
		}
	}
}

Result: 19406


made with vanilla PHP and MySQL, no frameworks, no bootstrap, no unnecessary* javascript