Wo part two was weird: I noticed some pattern appearing in regular intervals (with some initial offset). One was kind of vertical (every 103 frames) and one horizontal (every 101 frames). so I only printed frames, that were multiples of these intervals (plus offset). That enabled me to find it quite quickly
Solution 1
const std = @import("std");
//const input = @embedFile("./small.inp");
const input = @embedFile("./big.inp");
const ndim: usize = 2;
const robot_t = struct {
pos: [ndim]i32,
vel: [ndim]i32,
const Self = @This();
pub fn apply_pbc(self: *Self, nx: usize, ny: usize) void {
while (self.pos[0] >= nx) {
self.pos[0] -= @as(i32, @intCast(nx));
}
while (self.pos[0] < 0) {
self.pos[0] += @as(i32, @intCast(nx));
}
while (self.pos[1] >= ny) {
self.pos[1] -= @as(i32, @intCast(ny));
}
while (self.pos[1] < 0) {
self.pos[1] += @as(i32, @intCast(ny));
}
}
pub fn step(self: *Self) void {
for (0..ndim) |idim| {
self.pos[idim] += self.vel[idim];
}
}
};
const floor_t = struct {
nx: usize,
ny: usize,
robots: std.ArrayList(robot_t),
const Self = @This();
pub fn init(nx: usize, ny: usize, inputstr: []const u8, allocator: std.mem.Allocator) Self {
var floor = Self{
.nx = nx,
.ny = ny,
.robots = std.ArrayList(robot_t).init(allocator),
};
floor.read_robots(inputstr) catch |err| {
std.debug.print("{?}\n", .{err});
};
return floor;
}
pub fn deinit(self: *Self) void {
self.nx = 0;
self.ny = 0;
_ = self.robots.deinit();
}
pub fn read_robots(self: *Self, inputstr: []const u8) !void {
var lines = std.mem.tokenizeScalar(u8, inputstr, '\n');
while (lines.next()) |line| {
var robot = robot_t{ .pos = undefined, .vel = undefined };
var fields = std.mem.tokenizeScalar(u8, line, ' ');
const posfield = fields.next() orelse "";
var coords = std.mem.tokenizeScalar(u8, posfield[2..], ',');
var i: usize = 0;
while (coords.next()) |val| : (i += 1) {
robot.pos[i] = try std.fmt.parseInt(i32, val, 10);
}
const velfield = fields.next() orelse "";
coords = std.mem.tokenizeScalar(u8, velfield[2..], ',');
i = 0;
while (coords.next()) |val| : (i += 1) {
robot.vel[i] = try std.fmt.parseInt(i32, val, 10);
}
try self.robots.append(robot);
}
return;
}
pub fn apply_pbc(self: *Self) void {
for (self.robots.items) |*robot| {
robot.apply_pbc(self.nx, self.ny);
}
}
pub fn step(self: *Self) void {
for (self.robots.items) |*robot| {
robot.step();
}
}
pub fn print(self: Self) void {
for (0..self.ny) |y| {
for (0..self.nx) |x| {
var nfound: i32 = 0;
for (self.robots.items) |robot| {
if (robot.pos[0] == x and robot.pos[1] == y) {
nfound += 1;
}
}
if (nfound > 0) {
std.debug.print("{d}", .{nfound});
} else {
std.debug.print(".", .{});
}
}
std.debug.print("\n", .{});
}
}
pub fn safety(self: Self) usize {
var nNorthWest: usize = 0;
var nNorthEast: usize = 0;
var nSouthWest: usize = 0;
var nSouthEast: usize = 0;
for (self.robots.items) |robot| {
if (robot.pos[0] < self.nx / 2 and robot.pos[1] < self.ny / 2) {
nNorthWest += 1;
}
if (robot.pos[0] > self.nx / 2 and robot.pos[1] < self.ny / 2) {
nNorthEast += 1;
}
if (robot.pos[0] < self.nx / 2 and robot.pos[1] > self.ny / 2) {
nSouthWest += 1;
}
if (robot.pos[0] > self.nx / 2 and robot.pos[1] > self.ny / 2) {
nSouthEast += 1;
}
}
return nNorthWest * nNorthEast * nSouthWest * nSouthEast;
}
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var floor = floor_t.init(101, 103, input, allocator);
//var floor = floor_t.init(11, 7, input, allocator);
defer _ = floor.deinit();
floor.print();
std.debug.print("\n", .{});
for (0..100) |_| {
floor.step();
}
floor.apply_pbc();
floor.print();
const safety = floor.safety();
std.debug.print("Safety value: {d}\n", .{safety});
}
Solution 2
const std = @import("std");
//const input = @embedFile("./small.inp");
const input = @embedFile("./big.inp");
const ndim: usize = 2;
const robot_t = struct {
pos: [ndim]i32,
vel: [ndim]i32,
const Self = @This();
pub fn apply_pbc(self: *Self, nx: usize, ny: usize) void {
while (self.pos[0] >= nx) {
self.pos[0] -= @as(i32, @intCast(nx));
}
while (self.pos[0] < 0) {
self.pos[0] += @as(i32, @intCast(nx));
}
while (self.pos[1] >= ny) {
self.pos[1] -= @as(i32, @intCast(ny));
}
while (self.pos[1] < 0) {
self.pos[1] += @as(i32, @intCast(ny));
}
}
pub fn step(self: *Self) void {
for (0..ndim) |idim| {
self.pos[idim] += self.vel[idim];
}
}
};
const floor_t = struct {
nx: usize,
ny: usize,
robots: std.ArrayList(robot_t),
const Self = @This();
pub fn init(nx: usize, ny: usize, inputstr: []const u8, allocator: std.mem.Allocator) Self {
var floor = Self{
.nx = nx,
.ny = ny,
.robots = std.ArrayList(robot_t).init(allocator),
};
floor.read_robots(inputstr) catch |err| {
std.debug.print("{?}\n", .{err});
};
return floor;
}
pub fn deinit(self: *Self) void {
self.nx = 0;
self.ny = 0;
_ = self.robots.deinit();
}
pub fn read_robots(self: *Self, inputstr: []const u8) !void {
var lines = std.mem.tokenizeScalar(u8, inputstr, '\n');
while (lines.next()) |line| {
var robot = robot_t{ .pos = undefined, .vel = undefined };
var fields = std.mem.tokenizeScalar(u8, line, ' ');
const posfield = fields.next() orelse "";
var coords = std.mem.tokenizeScalar(u8, posfield[2..], ',');
var i: usize = 0;
while (coords.next()) |val| : (i += 1) {
robot.pos[i] = try std.fmt.parseInt(i32, val, 10);
}
const velfield = fields.next() orelse "";
coords = std.mem.tokenizeScalar(u8, velfield[2..], ',');
i = 0;
while (coords.next()) |val| : (i += 1) {
robot.vel[i] = try std.fmt.parseInt(i32, val, 10);
}
try self.robots.append(robot);
}
return;
}
pub fn apply_pbc(self: *Self) void {
for (self.robots.items) |*robot| {
robot.apply_pbc(self.nx, self.ny);
}
}
pub fn step(self: *Self) void {
for (self.robots.items) |*robot| {
robot.step();
}
}
pub fn print(self: Self) void {
for (0..self.ny) |y| {
for (0..self.nx) |x| {
var nfound: i32 = 0;
for (self.robots.items) |robot| {
if (robot.pos[0] == x and robot.pos[1] == y) {
nfound += 1;
}
}
if (nfound > 0) {
std.debug.print("{d}", .{nfound});
} else {
std.debug.print(".", .{});
}
}
std.debug.print("\n", .{});
}
}
pub fn is_symmetric(self: Self) bool {
var nsymmetric: usize = 0;
var nasymmetric: usize = 0;
const nxhalf = @divFloor(@as(i32, @intCast(self.nx)), 2);
for (self.robots.items[0 .. self.robots.items.len - 1], 0..) |robot1, i| {
var found_mirror = false;
for (self.robots.items[i + 1 ..]) |robot2| {
if (robot1.pos[1] == robot2.pos[1]) {
if (nxhalf - robot1.pos[0] == -1 * (nxhalf - robot2.pos[0])) {
found_mirror = true;
break;
}
}
}
if (!found_mirror) {
nasymmetric += 1;
} else {
nsymmetric += 1;
}
}
const limit = 50;
if (@divFloor(100 * nsymmetric, 100 * (nasymmetric + nsymmetric)) >= limit) {
return true;
} else {
return false;
}
}
pub fn empty_line(self: Self, linenum: i32) bool {
for (self.robots.items) |robot| {
if (robot.pos[0] == linenum) {
return false;
}
}
return true;
}
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var floor = floor_t.init(101, 103, input, allocator);
//var floor = floor_t.init(11, 7, input, allocator);
defer _ = floor.deinit();
floor.print();
for (0..10000) |istep| {
floor.step();
floor.apply_pbc();
if (istep >= 85) {
if (@mod(istep - 50, 103) == 0 or @mod(istep - 85, 101) == 0) {
std.debug.print("\n{d}\n", .{istep + 1});
floor.print();
}
}
}
}