const std = @import("std"); pub fn loadFromStream(alloc: *std.mem.Allocator, stream: *std.fs.File.InStream.Stream) anyerror![]i32 { var program = try alloc.alloc(i32, 1024); var buf = [_]u8{0} ** 255; var i: u32 = 0; while (try stream.readUntilDelimiterOrEof(&buf, ',')) |num| { var trimmed = std.mem.trimRight(u8, num, "\r\n"); program[i] = try std.fmt.parseInt(i32, trimmed, 10); std.debug.warn("{},", program[i]); i += 1; } std.debug.warn("\n"); return program[0..program.len]; } pub fn loadFromStdIn(alloc: *std.mem.Allocator) anyerror![]i32 { const file = std.io.getStdIn(); const stream = &file.inStream().stream; return loadFromStream(alloc, stream); } pub const Machine = struct { memory: []i32, ip: usize = 0, pub fn Run(program: []i32) anyerror!Machine { var machine = try init(program); machine.run(); return machine; } pub fn init(program: []i32) anyerror!Machine { return Machine{ .memory = program }; } pub fn run(self: *Machine) void { while (self.step()) {} } pub fn step(self: *Machine) bool { var program = self.memory; const opcode = switch (program[self.ip]) { 1 => { var a = program[@intCast(usize, program[self.ip + 1])]; var b = program[@intCast(usize, program[self.ip + 2])]; var dest = program[self.ip + 3]; var result = a + b; program[@intCast(usize, dest)] = result; self.ip += 4; }, 2 => { var a = program[@intCast(usize, program[self.ip + 1])]; var b = program[@intCast(usize, program[self.ip + 2])]; var dest = program[self.ip + 3]; var result = a * b; program[@intCast(usize, dest)] = result; self.ip += 4; }, 99 => { self.ip += 1; return false; }, else => { std.debug.warn("Unknown opcode at IP {}: {}", self.ip, program[self.ip]); return false; }, }; return true; } }; const assert = std.debug.assert; test "day 2 example 1" { var before: [12]i32 = .{ 1, 9, 10, 3, 2, 3, 11, 0, 99, 30, 40, 50 }; var after: [12]i32 = .{ 3500, 9, 10, 70, 2, 3, 11, 0, 99, 30, 40, 50 }; var machine = Machine.Run(before[0..before.len]); assert(std.mem.eql(i32, before[0..before.len], after[0..after.len])); } test "day 2 example 2" { var before: [5]i32 = .{ 1, 0, 0, 0, 99 }; var after: [5]i32 = .{ 2, 0, 0, 0, 99 }; var machine = Machine.Run(before[0..before.len]); assert(std.mem.eql(i32, before[0..before.len], after[0..after.len])); } test "day 2 example 3" { var before: [5]i32 = .{ 2, 3, 0, 3, 99 }; var after: [5]i32 = .{ 2, 3, 0, 6, 99 }; var machine = Machine.Run(before[0..before.len]); assert(std.mem.eql(i32, before[0..before.len], after[0..after.len])); } test "day 2 example 4" { var before: [6]i32 = .{ 2, 4, 4, 5, 99, 0 }; var after: [6]i32 = .{ 2, 4, 4, 5, 99, 9801 }; var machine = Machine.Run(before[0..before.len]); assert(std.mem.eql(i32, before[0..before.len], after[0..after.len])); } test "day 2 example 5" { var before: [9]i32 = .{ 1, 1, 1, 4, 99, 5, 6, 0, 99 }; var after: [9]i32 = .{ 30, 1, 1, 4, 2, 5, 6, 0, 99 }; var machine = Machine.Run(before[0..before.len]); assert(std.mem.eql(i32, before[0..before.len], after[0..after.len])); }