Files
advent-of-code/lib/intcode/intcode.zig

324 lines
8.8 KiB
Zig
Raw Normal View History

const std = @import("std");
2019-12-05 22:31:18 +00:00
const CharDev = std.ArrayList(i32);
const CharDevLimit = 32;
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);
}
2019-12-06 00:10:14 +00:00
const Opcode = enum(u8) {
ADD = 1,
MULT = 2,
IN = 3,
OUT = 4,
2019-12-06 00:44:48 +00:00
JNZ = 5,
JZ = 6,
LT = 7,
EQ = 8,
2019-12-06 00:10:14 +00:00
END = 99,
};
const Mode = enum(u8) {
Position = 0,
Immediate = 1,
};
const Instruction = struct {
op: Opcode,
// Modes for each parameter of the opcode. Parameter 0 is in position 0, etc
modes: [3]Mode,
pub fn decode(value: i32) !Instruction {
assert(value > 0 and value < 99999);
var buf: [6]u8 = undefined;
const str = try std.fmt.bufPrint(&buf, "{d:0<6}", value);
const op = ((str[4] - '0') * 10) + (str[5] - '0');
return Instruction{
.op = @intToEnum(Opcode, op),
.modes = [3]Mode{
@intToEnum(Mode, buf[3] - '0'),
@intToEnum(Mode, buf[2] - '0'),
@intToEnum(Mode, buf[1] - '0'),
},
};
}
/// Number of bytes taken up by a particular instruction
pub fn size(self: Instruction) usize {
return switch (self.op) {
.ADD => 4,
.MULT => 4,
.IN => 2,
.OUT => 2,
2019-12-06 00:44:48 +00:00
.JNZ => 3,
.JZ => 3,
.LT => 4,
.EQ => 4,
2019-12-06 00:10:14 +00:00
.END => 1,
};
}
pub fn halt(self: Instruction) bool {
return self.op == .END;
}
};
2019-12-05 22:07:05 +00:00
pub const Machine = struct {
memory: []i32,
2019-12-05 22:07:05 +00:00
ip: usize = 0,
2019-12-05 22:31:18 +00:00
input: CharDev,
output: CharDev,
pub fn Run(alloc: *std.mem.Allocator, program: []i32) anyerror!Machine {
var machine = try init(alloc, program);
2019-12-05 22:07:05 +00:00
2019-12-05 22:31:18 +00:00
try machine.run();
2019-12-05 22:07:05 +00:00
return machine;
}
2019-12-05 22:31:18 +00:00
pub fn init(alloc: *std.mem.Allocator, program: []i32) anyerror!Machine {
return Machine{
.memory = program,
.input = try CharDev.initCapacity(alloc, CharDevLimit),
.output = try CharDev.initCapacity(alloc, CharDevLimit),
};
2019-12-05 22:07:05 +00:00
}
2019-12-05 22:31:18 +00:00
pub fn run(self: *Machine) anyerror!void {
while (try self.step()) {}
2019-12-05 22:07:05 +00:00
}
2019-12-06 00:10:14 +00:00
/// Read an immediate or position value from memory. Parameter determines
/// which field following the current instruction to read from
inline fn __read(self: *Machine, parameter: usize, mode: Mode) i32 {
const immediate = self.memory[self.ip + parameter + 1];
return switch (mode) {
.Immediate => immediate,
.Position => self.memory[@intCast(usize, immediate)],
};
}
inline fn __write(self: *Machine, parameter: usize, value: i32) void {
const dest = self.__read(parameter, .Immediate);
self.memory[@intCast(usize, dest)] = value;
}
2019-12-05 22:31:18 +00:00
pub fn step(self: *Machine) anyerror!bool {
2019-12-06 00:44:48 +00:00
const insn = try Instruction.decode(self.memory[self.ip]);
const start_ip = self.ip;
2019-12-05 22:07:05 +00:00
2019-12-06 00:10:14 +00:00
const opcode = switch (insn.op) {
.ADD => {
const a = self.__read(0, insn.modes[0]);
const b = self.__read(1, insn.modes[1]);
2019-12-06 00:10:14 +00:00
self.__write(2, a + b);
},
2019-12-05 22:45:18 +00:00
2019-12-06 00:10:14 +00:00
.MULT => {
const a = self.__read(0, insn.modes[0]);
const b = self.__read(1, insn.modes[1]);
2019-12-05 22:45:18 +00:00
2019-12-06 00:10:14 +00:00
self.__write(2, a * b);
2019-12-05 22:45:18 +00:00
},
2019-12-06 00:10:14 +00:00
.IN => {
self.__write(0, self.input.orderedRemove(0));
},
2019-12-06 00:44:48 +00:00
2019-12-06 00:10:14 +00:00
.OUT => {
try self.output.append(self.__read(0, insn.modes[0]));
},
2019-12-06 00:44:48 +00:00
.JNZ => {
if (self.__read(0, insn.modes[0]) != 0)
self.ip = @intCast(usize, self.__read(1, insn.modes[1]));
},
.JZ => {
if (self.__read(0, insn.modes[0]) == 0)
self.ip = @intCast(usize, self.__read(1, insn.modes[1]));
},
.LT => {
const a = self.__read(0, insn.modes[0]);
const b = self.__read(1, insn.modes[1]);
if (a < b) {
self.__write(2, 1);
} else {
self.__write(2, 0);
}
},
.EQ => {
const a = self.__read(0, insn.modes[0]);
const b = self.__read(1, insn.modes[1]);
if (a == b) {
self.__write(2, 1);
} else {
self.__write(2, 0);
}
},
2019-12-06 00:10:14 +00:00
.END => {},
};
2019-12-05 22:07:05 +00:00
2019-12-06 00:44:48 +00:00
// Only modify IP if the instruction itself has not
if (self.ip == start_ip) self.ip += insn.size();
2019-12-06 00:10:14 +00:00
return !insn.halt();
}
2019-12-05 22:07:05 +00:00
};
const assert = std.debug.assert;
2019-12-05 22:31:18 +00:00
const test_allocator = std.heap.page_allocator;
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 };
2019-12-05 22:31:18 +00:00
var machine = try Machine.Run(test_allocator, 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 };
2019-12-05 22:31:18 +00:00
var machine = try Machine.Run(test_allocator, 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 };
2019-12-05 22:31:18 +00:00
var machine = try Machine.Run(test_allocator, 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 };
2019-12-05 22:31:18 +00:00
var machine = try Machine.Run(test_allocator, 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 };
2019-12-05 22:31:18 +00:00
var machine = try Machine.Run(test_allocator, before[0..before.len]);
assert(std.mem.eql(i32, before[0..before.len], after[0..after.len]));
}
test "day 5 example 1" {
var before: [5]i32 = .{ 3, 0, 4, 0, 99 };
2019-12-05 22:45:18 +00:00
var after: [5]i32 = .{ 666, 0, 4, 0, 99 };
2019-12-05 22:31:18 +00:00
var machine = try Machine.init(test_allocator, before[0..before.len]);
try machine.input.append(666);
try machine.run();
assert(std.mem.eql(i32, before[0..before.len], after[0..after.len]));
2019-12-05 22:31:18 +00:00
assert(machine.output.len == 1);
assert(machine.output.at(0) == 666);
}
2019-12-06 00:10:14 +00:00
test "Instruction#decode ADD" {
const insn = try Instruction.decode(1);
assert(insn.op == Opcode.ADD);
assert(insn.modes[0] == .Position);
assert(insn.modes[1] == .Position);
assert(insn.modes[2] == .Position);
}
test "Instruction#decode MULT" {
const insn = try Instruction.decode(2);
assert(insn.op == Opcode.MULT);
assert(insn.modes[0] == .Position);
assert(insn.modes[1] == .Position);
assert(insn.modes[2] == .Position);
}
test "Instruction#decode IN" {
const insn = try Instruction.decode(3);
assert(insn.op == Opcode.IN);
assert(insn.modes[0] == .Position);
assert(insn.modes[1] == .Position);
assert(insn.modes[2] == .Position);
}
test "Instruction#decode OUT" {
const insn = try Instruction.decode(4);
assert(insn.op == Opcode.OUT);
assert(insn.modes[0] == .Position);
assert(insn.modes[1] == .Position);
assert(insn.modes[2] == .Position);
}
test "Instruction#decode END Position" {
const insn = try Instruction.decode(99);
assert(insn.op == Opcode.END);
assert(insn.modes[0] == .Position);
assert(insn.modes[1] == .Position);
assert(insn.modes[2] == .Position);
}
test "Instruction#decode END Immediate" {
const insn = try Instruction.decode(11199);
assert(insn.op == Opcode.END);
assert(insn.modes[0] == .Immediate);
assert(insn.modes[1] == .Immediate);
assert(insn.modes[2] == .Immediate);
}
test "Instruction#decode END mixed" {
const insn = try Instruction.decode(10099);
assert(insn.op == Opcode.END);
assert(insn.modes[0] == .Position);
assert(insn.modes[1] == .Position);
assert(insn.modes[2] == .Immediate);
}