diff --git a/05/src/main.zig b/05/src/main.zig index 18353e7..33b581c 100644 --- a/05/src/main.zig +++ b/05/src/main.zig @@ -18,16 +18,12 @@ pub fn main() anyerror!void { try m1.run(); var out: i32 = 0; - while (m1.readFromOutput(false)) |item| { - out = item; - } else |err| { - std.debug.assert(err == std.os.ReadError.WouldBlock); - } + while (out == 0) : (out = try m1.readFromOutput()) {} std.debug.warn("Day 5, Part 1: {}\n", out); try m2.writeToInput(5); // Thermal radiator controller try m2.run(); - std.debug.warn("Day 5, Part 2: {}\n", try m2.readFromOutput(false)); + std.debug.warn("Day 5, Part 2: {}\n", try m2.readFromOutput()); } diff --git a/07/src/main.zig b/07/src/main.zig index a3ef2cb..b71a8d5 100644 --- a/07/src/main.zig +++ b/07/src/main.zig @@ -136,42 +136,74 @@ pub fn main() anyerror!void { // 5 intcode computers act as the amps var machines: [5]intcode.Machine = undefined; + // Allocate memory once only. + for (machines) |*machine| { + machine.alloc = alloc; + machine.memory = try alloc.alloc(i32, program.len); + } + + // Now wire up the machines correctly. The output of each machine should be + // the input to the next. + const pipes: [6]intcode.CharDev = .{ + try std.os.pipe(), + try std.os.pipe(), + try std.os.pipe(), + try std.os.pipe(), + try std.os.pipe(), + try std.os.pipe(), + }; + + machines[0].input = pipes[0]; + machines[0].output = pipes[1]; + + machines[1].input = machines[0].output; + machines[1].output = pipes[2]; + + machines[2].input = machines[1].output; + machines[2].output = pipes[3]; + + machines[3].input = machines[2].output; + machines[3].output = pipes[4]; + + machines[4].input = machines[3].output; + machines[4].output = pipes[5]; + // 120 phase permutations. Find the highest. var max: i32 = 0; for (perm1) |inputs| { for (machines) |*machine| { - var copy = try alloc.alloc(i32, program.len); - std.mem.copy(i32, copy, program); - - machine.* = try intcode.Machine.init(alloc, copy); + std.mem.copy(i32, machine.memory, program); + machine.ip = 0; } - // Phase input + // Phase inputs for (inputs) |input, i| { try machines[i].writeToInput(input); } - // amplifier input - for (machines) |*machine, i| { - var ampInput: i32 = 0; - if (i > 0) ampInput = try machines[i - 1].readFromOutput(false); + // Provide starting value of 0 + try machines[0].writeToInput(0); - try machine.writeToInput(ampInput); + // run the program + for (machines) |*machine, i| { try machine.run(); } - const final = try machines[4].readFromOutput(false); + const final = try machines[4].readFromOutput(); if (final > max) max = final; // Don't forget to free everything! for (machines) |*machine, i| { alloc.free(machine.memory); - machine.deinit(); } } std.debug.warn("Day 7, Part 1: {}\n", max); - // In part 2, the machines need to be wired into a feedback loop + // In part 2, the machines need to be wired into a feedback loop. + // To do this, drop pipe 6 and replace it with pipe 0 + std.os.close(pipes[5][0]); + std.os.close(pipes[5][1]); + machines[4].output = machines[0].input; } diff --git a/lib/intcode/intcode.zig b/lib/intcode/intcode.zig index 2a795eb..471ec41 100644 --- a/lib/intcode/intcode.zig +++ b/lib/intcode/intcode.zig @@ -1,6 +1,7 @@ const std = @import("std"); -const CharDev = std.atomic.Queue(i32); +// We're using pipes as character devices +pub const CharDev = [2]i32; pub fn loadFromStream(alloc: *std.mem.Allocator, stream: *std.fs.File.InStream.Stream) anyerror![]i32 { var program = try alloc.alloc(i32, 1024); @@ -111,8 +112,8 @@ pub const Machine = struct { return Machine{ .alloc = alloc, .memory = program, - .input = CharDev.init(), - .output = CharDev.init(), + .input = try std.os.pipe(), + .output = try std.os.pipe(), }; } @@ -121,47 +122,43 @@ pub const Machine = struct { } pub fn deinit(self: *Machine) void { - // TODO + std.os.close(self.input[0]); + std.os.close(self.input[1]); + std.os.close(self.output[0]); + std.os.close(self.output[1]); } - pub fn readFromInput(self: *Machine, blocking: bool) !i32 { - return self.__in(&self.input, blocking); + pub fn readFromInput(self: *Machine) !i32 { + return self.__in(self.input); } pub fn writeToInput(self: *Machine, val: i32) !void { - return self.__out(&self.input, val); + return self.__out(self.input, val); } - pub fn readFromOutput(self: *Machine, blocking: bool) !i32 { - return self.__in(&self.output, blocking); + pub fn readFromOutput(self: *Machine) !i32 { + return self.__in(self.output); } pub fn writeToOutput(self: *Machine, val: i32) !void { - return self.__out(&self.output, val); + return self.__out(self.output, val); } - inline fn __in(self: *Machine, dev: *CharDev, blocking: bool) !i32 { - var node = dev.get(); + inline fn __in(self: *Machine, dev: CharDev) !i32 { + const fd = dev[0]; // Read from the pipe + var store: [4]u8 = undefined; - if (node == null) { - if (!blocking) return std.os.ReadError.WouldBlock; - // Just busy-wait for now - while (node == null) node = dev.get(); - } + var n = try std.os.read(fd, &store); + std.debug.assert(n == 4); // TODO: handle partial reads - if (node) |n| { - defer self.alloc.destroy(n); - return n.data; - } - - unreachable; + return std.mem.readIntNative(i32, &store); } - inline fn __out(self: *Machine, dev: *CharDev, val: i32) !void { - var node = try self.alloc.create(CharDev.Node); - node.data = val; + inline fn __out(self: *Machine, dev: CharDev, val: i32) !void { + const fd = dev[1]; // Write to the pipe + const bytes = std.mem.asBytes(&val); - dev.put(node); + try std.os.write(fd, bytes); } /// Read an immediate or position value from memory. Parameter determines @@ -200,7 +197,7 @@ pub const Machine = struct { }, .IN => { - self.__write(0, try self.readFromInput(true)); // blocking read + self.__write(0, try self.readFromInput()); // blocking read }, .OUT => { @@ -301,7 +298,7 @@ test "day 5 example 1" { try machine.run(); assert(std.mem.eql(i32, before[0..before.len], after[0..after.len])); - assert(666 == try machine.readFromOutput(false)); + assert(666 == try machine.readFromOutput()); } test "Instruction#decode ADD" {