Finish day 17
This commit is contained in:
parent
dcc4dfce6a
commit
b28fd56a8a
|
@ -0,0 +1,27 @@
|
||||||
|
const Builder = @import("std").build.Builder;
|
||||||
|
|
||||||
|
pub fn build(b: *Builder) void {
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// Standard release options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||||
|
const mode = b.standardReleaseOptions();
|
||||||
|
|
||||||
|
const exe = b.addExecutable("day17", "src/main.zig");
|
||||||
|
exe.setTarget(target);
|
||||||
|
exe.setBuildMode(mode);
|
||||||
|
exe.install();
|
||||||
|
|
||||||
|
const run_cmd = exe.run();
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
####...#
|
||||||
|
......#.
|
||||||
|
#..#.##.
|
||||||
|
.#...#.#
|
||||||
|
..###.#.
|
||||||
|
##.###..
|
||||||
|
.#...###
|
||||||
|
.##....#
|
|
@ -0,0 +1,633 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn main() anyerror!void {
|
||||||
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
var gpa = &arena.allocator;
|
||||||
|
|
||||||
|
var f = try std.fs.cwd().openFile("input", .{});
|
||||||
|
var contents = try f.readToEndAlloc(gpa, std.math.maxInt(u64));
|
||||||
|
defer gpa.free(contents);
|
||||||
|
|
||||||
|
var sim = try Simulation.init(gpa);
|
||||||
|
defer sim.deinit();
|
||||||
|
|
||||||
|
var sim4d = try Simulation4D.init(gpa);
|
||||||
|
defer sim4d.deinit();
|
||||||
|
|
||||||
|
var it = std.mem.tokenize(contents, "\n");
|
||||||
|
var y : i64 = 0;
|
||||||
|
while (it.next()) |line| {
|
||||||
|
for (line) |c, k| {
|
||||||
|
var state : CubeState = .inactive;
|
||||||
|
if (c == '#') {
|
||||||
|
state = .active;
|
||||||
|
}
|
||||||
|
try sim.add_cube([_]i64 {@intCast(i64, k), y, 0}, state);
|
||||||
|
try sim4d.add_cube([_]i64 {@intCast(i64, k), y, 0, 0}, state);
|
||||||
|
}
|
||||||
|
y += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run 6 rounds
|
||||||
|
try sim.do_round(); // 1
|
||||||
|
try sim.do_round(); // 2
|
||||||
|
try sim.do_round(); // 3
|
||||||
|
try sim.do_round(); // 4
|
||||||
|
try sim.do_round(); // 5
|
||||||
|
try sim.do_round(); // 6
|
||||||
|
|
||||||
|
std.log.info("After 6 rounds, there are {} active cubes",
|
||||||
|
.{sim.count_active_cubes()});
|
||||||
|
|
||||||
|
// Part 2
|
||||||
|
try sim4d.do_round(); // 1
|
||||||
|
try sim4d.do_round(); // 2
|
||||||
|
try sim4d.do_round(); // 3
|
||||||
|
try sim4d.do_round(); // 4
|
||||||
|
try sim4d.do_round(); // 5
|
||||||
|
try sim4d.do_round(); // 6
|
||||||
|
|
||||||
|
std.log.info("After 6 rounds, there are {} active cubes",
|
||||||
|
.{sim4d.count_active_cubes()});
|
||||||
|
}
|
||||||
|
|
||||||
|
const CubeState = enum {
|
||||||
|
inactive,
|
||||||
|
active
|
||||||
|
};
|
||||||
|
|
||||||
|
const Cube = struct {
|
||||||
|
pos: [3]i64,
|
||||||
|
state: CubeState = .inactive,
|
||||||
|
next_state: ?CubeState = null,
|
||||||
|
neighbours: u64 = 0,
|
||||||
|
|
||||||
|
pub fn get_neighbour_positions(self: *Cube) [26][3]i64 {
|
||||||
|
return [_][3]i64 {
|
||||||
|
// z-1
|
||||||
|
[_]i64 {self.pos[0], self.pos[1], self.pos[2]-1},
|
||||||
|
[_]i64 {self.pos[0], self.pos[1]+1, self.pos[2]-1},
|
||||||
|
[_]i64 {self.pos[0], self.pos[1]-1, self.pos[2]-1},
|
||||||
|
[_]i64 {self.pos[0]-1, self.pos[1], self.pos[2]-1},
|
||||||
|
[_]i64 {self.pos[0]-1, self.pos[1]+1, self.pos[2]-1},
|
||||||
|
[_]i64 {self.pos[0]-1, self.pos[1]-1, self.pos[2]-1},
|
||||||
|
[_]i64 {self.pos[0]+1, self.pos[1], self.pos[2]-1},
|
||||||
|
[_]i64 {self.pos[0]+1, self.pos[1]+1, self.pos[2]-1},
|
||||||
|
[_]i64 {self.pos[0]+1, self.pos[1]-1, self.pos[2]-1},
|
||||||
|
// z+0
|
||||||
|
[_]i64 {self.pos[0], self.pos[1]+1, self.pos[2]},
|
||||||
|
[_]i64 {self.pos[0], self.pos[1]-1, self.pos[2]},
|
||||||
|
[_]i64 {self.pos[0]-1, self.pos[1], self.pos[2]},
|
||||||
|
[_]i64 {self.pos[0]-1, self.pos[1]+1, self.pos[2]},
|
||||||
|
[_]i64 {self.pos[0]-1, self.pos[1]-1, self.pos[2]},
|
||||||
|
[_]i64 {self.pos[0]+1, self.pos[1], self.pos[2]},
|
||||||
|
[_]i64 {self.pos[0]+1, self.pos[1]+1, self.pos[2]},
|
||||||
|
[_]i64 {self.pos[0]+1, self.pos[1]-1, self.pos[2]},
|
||||||
|
// z+1
|
||||||
|
[_]i64 {self.pos[0], self.pos[1], self.pos[2]+1},
|
||||||
|
[_]i64 {self.pos[0], self.pos[1]+1, self.pos[2]+1},
|
||||||
|
[_]i64 {self.pos[0], self.pos[1]-1, self.pos[2]+1},
|
||||||
|
[_]i64 {self.pos[0]-1, self.pos[1], self.pos[2]+1},
|
||||||
|
[_]i64 {self.pos[0]-1, self.pos[1]+1, self.pos[2]+1},
|
||||||
|
[_]i64 {self.pos[0]-1, self.pos[1]-1, self.pos[2]+1},
|
||||||
|
[_]i64 {self.pos[0]+1, self.pos[1], self.pos[2]+1},
|
||||||
|
[_]i64 {self.pos[0]+1, self.pos[1]+1, self.pos[2]+1},
|
||||||
|
[_]i64 {self.pos[0]+1, self.pos[1]-1, self.pos[2]+1},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_state(self: *Cube) !void {
|
||||||
|
var stdout = std.io.getStdOut().writer();
|
||||||
|
var c : u8 = '.';
|
||||||
|
if (self.state == .active) {
|
||||||
|
c = '#';
|
||||||
|
}
|
||||||
|
try stdout.print("({}x, {}y, {}z) {c}\n", .{self.pos[0], self.pos[1], self.pos[2], c});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Simulation = struct {
|
||||||
|
map: std.hash_map.AutoHashMap([3]i64, Cube),
|
||||||
|
allocator: *std.mem.Allocator,
|
||||||
|
|
||||||
|
pub fn init(allocator: *std.mem.Allocator) !*Simulation {
|
||||||
|
var self = try allocator.create(Simulation);
|
||||||
|
errdefer allocator.destroy(self);
|
||||||
|
|
||||||
|
self.* = Simulation {
|
||||||
|
.map = std.hash_map.AutoHashMap([3]i64, Cube).init(allocator),
|
||||||
|
.allocator = allocator,
|
||||||
|
};
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Simulation) void {
|
||||||
|
self.map.deinit();
|
||||||
|
self.allocator.destroy(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_cube(self: *Simulation, pos: [3]i64, state: CubeState) !void {
|
||||||
|
try self.map.put(pos, Cube {.pos = pos, .state = state});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_state(self: *Simulation) !void {
|
||||||
|
// @TODO Organize the output somehow
|
||||||
|
var stdout = std.io.getStdOut().writer();
|
||||||
|
var z_min : i64 = std.math.maxInt(i64);
|
||||||
|
var z_max : i64 = std.math.minInt(i64);
|
||||||
|
var y_min : i64 = std.math.maxInt(i64);
|
||||||
|
var y_max : i64 = std.math.minInt(i64);
|
||||||
|
var x_min : i64 = std.math.maxInt(i64);
|
||||||
|
var x_max : i64 = std.math.minInt(i64);
|
||||||
|
|
||||||
|
var it = self.map.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
//try entry.value.print_state();
|
||||||
|
x_max = std.math.max(x_max, entry.value.pos[0]);
|
||||||
|
x_min = std.math.min(x_min, entry.value.pos[0]);
|
||||||
|
y_max = std.math.max(y_max, entry.value.pos[1]);
|
||||||
|
y_min = std.math.min(y_min, entry.value.pos[1]);
|
||||||
|
z_max = std.math.max(z_max, entry.value.pos[2]);
|
||||||
|
z_min = std.math.min(z_min, entry.value.pos[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For from lowest to highest then
|
||||||
|
var z_pos = z_min;
|
||||||
|
while (z_pos <= z_max) : (z_pos += 1) {
|
||||||
|
try stdout.print("z={} x=[{}..{}],y=[{}..{}]\n", .{z_pos, x_min, x_max, y_min, y_max});
|
||||||
|
var y_pos = y_min;
|
||||||
|
while (y_pos <= y_max) : (y_pos += 1) {
|
||||||
|
var x_pos = x_min;
|
||||||
|
while (x_pos <= x_max) : (x_pos += 1) {
|
||||||
|
//try stdout.print("({}, {}, {})\n", .{x_pos, y_pos, z_pos});
|
||||||
|
var c : u8 = '.';
|
||||||
|
if (self.map.get([_]i64{x_pos, y_pos, z_pos})) |cube| {
|
||||||
|
if (cube.state == .active) {
|
||||||
|
c = '#';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try stdout.print("{c}", .{c});
|
||||||
|
}
|
||||||
|
_ = try stdout.write("\n");
|
||||||
|
}
|
||||||
|
_ = try stdout.write("\n");
|
||||||
|
}
|
||||||
|
_ = try stdout.write("\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_state_neighbour_count (self: *Simulation) !void {
|
||||||
|
// @TODO Organize the output somehow
|
||||||
|
var stdout = std.io.getStdOut().writer();
|
||||||
|
var z_min : i64 = std.math.maxInt(i64);
|
||||||
|
var z_max : i64 = std.math.minInt(i64);
|
||||||
|
var y_min : i64 = std.math.maxInt(i64);
|
||||||
|
var y_max : i64 = std.math.minInt(i64);
|
||||||
|
var x_min : i64 = std.math.maxInt(i64);
|
||||||
|
var x_max : i64 = std.math.minInt(i64);
|
||||||
|
|
||||||
|
var it = self.map.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
//try entry.value.print_state();
|
||||||
|
x_max = std.math.max(x_max, entry.value.pos[0]);
|
||||||
|
x_min = std.math.min(x_min, entry.value.pos[0]);
|
||||||
|
y_max = std.math.max(y_max, entry.value.pos[1]);
|
||||||
|
y_min = std.math.min(y_min, entry.value.pos[1]);
|
||||||
|
z_max = std.math.max(z_max, entry.value.pos[2]);
|
||||||
|
z_min = std.math.min(z_min, entry.value.pos[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For from lowest to highest then
|
||||||
|
var z_pos = z_min;
|
||||||
|
while (z_pos <= z_max) : (z_pos += 1) {
|
||||||
|
try stdout.print("z={} x=[{}..{}],y=[{}..{}]\n", .{z_pos, x_min, x_max, y_min, y_max});
|
||||||
|
var y_pos = y_min;
|
||||||
|
while (y_pos <= y_max) : (y_pos += 1) {
|
||||||
|
var x_pos = x_min;
|
||||||
|
while (x_pos <= x_max) : (x_pos += 1) {
|
||||||
|
//try stdout.print("({}, {}, {})\n", .{x_pos, y_pos, z_pos});
|
||||||
|
var c : u64 = 0;
|
||||||
|
if (self.map.get([_]i64{x_pos, y_pos, z_pos})) |cube| {
|
||||||
|
c = cube.neighbours;
|
||||||
|
}
|
||||||
|
try stdout.print("{:02}", .{c});
|
||||||
|
}
|
||||||
|
_ = try stdout.write("\n");
|
||||||
|
}
|
||||||
|
_ = try stdout.write("\n");
|
||||||
|
}
|
||||||
|
_ = try stdout.write("\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn do_round(self: *Simulation) !void {
|
||||||
|
// For each cube, check all of it's neighbours
|
||||||
|
var it = self.map.iterator();
|
||||||
|
var neighbours_to_add = std.ArrayList([3]i64).init(self.allocator);
|
||||||
|
defer neighbours_to_add.deinit();
|
||||||
|
|
||||||
|
// Get the neighbours we need possibly create for this round
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
var neighbours = entry.value.get_neighbour_positions();
|
||||||
|
var n_active : u64 = 0;
|
||||||
|
for (neighbours) |n_pos| {
|
||||||
|
if (self.map.get(n_pos)) |n| {
|
||||||
|
// Noop, we already have a cube
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// The neighbour doesn't yet exist, we should create it
|
||||||
|
// but we don't want to do that while iterating over
|
||||||
|
// the current entries.
|
||||||
|
// We also want to make sure that we're not adding a
|
||||||
|
// value that's already in the list to be added.
|
||||||
|
// For the moment, we'll check that when iterating
|
||||||
|
// over neighbours_to_add.
|
||||||
|
try neighbours_to_add.append(n_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add any new neighbours
|
||||||
|
for (neighbours_to_add.items) |new_pos| {
|
||||||
|
// Double-check we don't already have this neighbour
|
||||||
|
if (self.map.get(new_pos)) |v| {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try self.map.putNoClobber(new_pos, Cube {.pos = new_pos, .state = .inactive});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.calculate_neighbour_states();
|
||||||
|
try self.print_state_neighbour_count();
|
||||||
|
|
||||||
|
// Now next state should be set, we go through again to swap
|
||||||
|
// @TODO Update state
|
||||||
|
it = self.map.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
if (entry.value.state == .active) {
|
||||||
|
if (entry.value.neighbours >= 2 and entry.value.neighbours <= 3) {
|
||||||
|
entry.value.state = .active;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
entry.value.state = .inactive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (entry.value.neighbours == 3) {
|
||||||
|
entry.value.state = .active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry.value.next_state = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_neighbour_states(self: *Simulation) void {
|
||||||
|
var it = self.map.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
var neighbours = entry.value.get_neighbour_positions();
|
||||||
|
var n_active : u64 = 0;
|
||||||
|
std.log.warn("Checking neighbours for ({}, {}, {})",
|
||||||
|
.{entry.value.pos[0], entry.value.pos[1], entry.value.pos[2]});
|
||||||
|
for (neighbours) |n_pos| {
|
||||||
|
if (self.map.get(n_pos)) |n| {
|
||||||
|
if (n.state == .active) {
|
||||||
|
std.log.warn("Neighbour at ({}, {}, {}) is active",
|
||||||
|
.{n.pos[0], n.pos[1], n.pos[2]});
|
||||||
|
n_active += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Noop, another function creates our neighbours, and it should
|
||||||
|
// be run before this one.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry.value.neighbours = n_active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count_active_cubes(self: *Simulation) u64 {
|
||||||
|
var it = self.map.iterator();
|
||||||
|
var count : u64 = 0;
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
if (entry.value.state == .active) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Part 2, is just more tedious
|
||||||
|
const Cube4D = struct {
|
||||||
|
pos: [4]i64,
|
||||||
|
state: CubeState = .inactive,
|
||||||
|
neighbours: u64 = 0,
|
||||||
|
|
||||||
|
pub fn get_neighbour_positions(self: *Cube4D, a: *std.mem.Allocator) [80][4]i64 {
|
||||||
|
var neighbours : [80][4]i64 = undefined;
|
||||||
|
var x = self.pos[0]-1;
|
||||||
|
var idx : usize = 0;
|
||||||
|
while (x <= self.pos[0]+1) : (x += 1) {
|
||||||
|
var y = self.pos[1]-1;
|
||||||
|
while (y <= self.pos[1]+1) : (y +=1) {
|
||||||
|
var z = self.pos[2]-1;
|
||||||
|
while (z <= self.pos[2]+1) : (z +=1) {
|
||||||
|
var w = self.pos[3]-1;
|
||||||
|
while (w <= self.pos[3]+1) : (w+=1) {
|
||||||
|
if (x == self.pos[0] and y == self.pos[1] and z == self.pos[2]
|
||||||
|
and w == self.pos[3]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
neighbours[idx] = [_]i64{x, y, z, w};
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return neighbours;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const Simulation4D = struct {
|
||||||
|
map: std.hash_map.AutoHashMap([4]i64, Cube4D),
|
||||||
|
allocator: *std.mem.Allocator,
|
||||||
|
|
||||||
|
pub fn init(allocator: *std.mem.Allocator) !*Simulation4D {
|
||||||
|
var self = try allocator.create(Simulation4D);
|
||||||
|
errdefer allocator.destroy(self);
|
||||||
|
|
||||||
|
self.* = Simulation4D {
|
||||||
|
.map = std.hash_map.AutoHashMap([4]i64, Cube4D).init(allocator),
|
||||||
|
.allocator = allocator,
|
||||||
|
};
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Simulation4D) void {
|
||||||
|
self.map.deinit();
|
||||||
|
self.allocator.destroy(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_cube(self: *Simulation4D, pos: [4]i64, state: CubeState) !void {
|
||||||
|
try self.map.put(pos, Cube4D {.pos = pos, .state = state});
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn print_state(self: *Simulation) !void {
|
||||||
|
// // @TODO Organize the output somehow
|
||||||
|
// var stdout = std.io.getStdOut().writer();
|
||||||
|
// var z_min : i64 = std.math.maxInt(i64);
|
||||||
|
// var z_max : i64 = std.math.minInt(i64);
|
||||||
|
// var y_min : i64 = std.math.maxInt(i64);
|
||||||
|
// var y_max : i64 = std.math.minInt(i64);
|
||||||
|
// var x_min : i64 = std.math.maxInt(i64);
|
||||||
|
// var x_max : i64 = std.math.minInt(i64);
|
||||||
|
|
||||||
|
// var it = self.map.iterator();
|
||||||
|
// while (it.next()) |entry| {
|
||||||
|
// //try entry.value.print_state();
|
||||||
|
// x_max = std.math.max(x_max, entry.value.pos[0]);
|
||||||
|
// x_min = std.math.min(x_min, entry.value.pos[0]);
|
||||||
|
// y_max = std.math.max(y_max, entry.value.pos[1]);
|
||||||
|
// y_min = std.math.min(y_min, entry.value.pos[1]);
|
||||||
|
// z_max = std.math.max(z_max, entry.value.pos[2]);
|
||||||
|
// z_min = std.math.min(z_min, entry.value.pos[2]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // For from lowest to highest then
|
||||||
|
// var z_pos = z_min;
|
||||||
|
// while (z_pos <= z_max) : (z_pos += 1) {
|
||||||
|
// try stdout.print("z={} x=[{}..{}],y=[{}..{}]\n", .{z_pos, x_min, x_max, y_min, y_max});
|
||||||
|
// var y_pos = y_min;
|
||||||
|
// while (y_pos <= y_max) : (y_pos += 1) {
|
||||||
|
// var x_pos = x_min;
|
||||||
|
// while (x_pos <= x_max) : (x_pos += 1) {
|
||||||
|
// //try stdout.print("({}, {}, {})\n", .{x_pos, y_pos, z_pos});
|
||||||
|
// var c : u8 = '.';
|
||||||
|
// if (self.map.get([_]i64{x_pos, y_pos, z_pos})) |cube| {
|
||||||
|
// if (cube.state == .active) {
|
||||||
|
// c = '#';
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// try stdout.print("{c}", .{c});
|
||||||
|
// }
|
||||||
|
// _ = try stdout.write("\n");
|
||||||
|
// }
|
||||||
|
// _ = try stdout.write("\n");
|
||||||
|
// }
|
||||||
|
// _ = try stdout.write("\n");
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn print_state_neighbour_count (self: *Simulation) !void {
|
||||||
|
// // @TODO Organize the output somehow
|
||||||
|
// var stdout = std.io.getStdOut().writer();
|
||||||
|
// var z_min : i64 = std.math.maxInt(i64);
|
||||||
|
// var z_max : i64 = std.math.minInt(i64);
|
||||||
|
// var y_min : i64 = std.math.maxInt(i64);
|
||||||
|
// var y_max : i64 = std.math.minInt(i64);
|
||||||
|
// var x_min : i64 = std.math.maxInt(i64);
|
||||||
|
// var x_max : i64 = std.math.minInt(i64);
|
||||||
|
|
||||||
|
// var it = self.map.iterator();
|
||||||
|
// while (it.next()) |entry| {
|
||||||
|
// //try entry.value.print_state();
|
||||||
|
// x_max = std.math.max(x_max, entry.value.pos[0]);
|
||||||
|
// x_min = std.math.min(x_min, entry.value.pos[0]);
|
||||||
|
// y_max = std.math.max(y_max, entry.value.pos[1]);
|
||||||
|
// y_min = std.math.min(y_min, entry.value.pos[1]);
|
||||||
|
// z_max = std.math.max(z_max, entry.value.pos[2]);
|
||||||
|
// z_min = std.math.min(z_min, entry.value.pos[2]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // For from lowest to highest then
|
||||||
|
// var z_pos = z_min;
|
||||||
|
// while (z_pos <= z_max) : (z_pos += 1) {
|
||||||
|
// try stdout.print("z={} x=[{}..{}],y=[{}..{}]\n", .{z_pos, x_min, x_max, y_min, y_max});
|
||||||
|
// var y_pos = y_min;
|
||||||
|
// while (y_pos <= y_max) : (y_pos += 1) {
|
||||||
|
// var x_pos = x_min;
|
||||||
|
// while (x_pos <= x_max) : (x_pos += 1) {
|
||||||
|
// //try stdout.print("({}, {}, {})\n", .{x_pos, y_pos, z_pos});
|
||||||
|
// var c : u64 = 0;
|
||||||
|
// if (self.map.get([_]i64{x_pos, y_pos, z_pos})) |cube| {
|
||||||
|
// c = cube.neighbours;
|
||||||
|
// }
|
||||||
|
// try stdout.print("{:02}", .{c});
|
||||||
|
// }
|
||||||
|
// _ = try stdout.write("\n");
|
||||||
|
// }
|
||||||
|
// _ = try stdout.write("\n");
|
||||||
|
// }
|
||||||
|
// _ = try stdout.write("\n");
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn do_round(self: *Simulation4D) !void {
|
||||||
|
// For each cube, check all of it's neighbours
|
||||||
|
var it = self.map.iterator();
|
||||||
|
var neighbours_to_add = std.ArrayList([4]i64).init(self.allocator);
|
||||||
|
defer neighbours_to_add.deinit();
|
||||||
|
|
||||||
|
// Get the neighbours we need possibly create for this round
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
var neighbours = entry.value.get_neighbour_positions(self.allocator);
|
||||||
|
//defer self.allocator.free(neighbours);
|
||||||
|
var n_active : u64 = 0;
|
||||||
|
for (neighbours) |n_pos| {
|
||||||
|
if (self.map.get(n_pos)) |n| {
|
||||||
|
// Noop, we already have a cube
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// The neighbour doesn't yet exist, we should create it
|
||||||
|
// but we don't want to do that while iterating over
|
||||||
|
// the current entries.
|
||||||
|
// We also want to make sure that we're not adding a
|
||||||
|
// value that's already in the list to be added.
|
||||||
|
// For the moment, we'll check that when iterating
|
||||||
|
// over neighbours_to_add.
|
||||||
|
try neighbours_to_add.append(n_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add any new neighbours
|
||||||
|
for (neighbours_to_add.items) |new_pos| {
|
||||||
|
// Double-check we don't already have this neighbour
|
||||||
|
if (self.map.get(new_pos)) |v| {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try self.map.putNoClobber(new_pos, Cube4D {.pos = new_pos, .state = .inactive});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.calculate_neighbour_states();
|
||||||
|
|
||||||
|
// Now next state should be set, we go through again to swap
|
||||||
|
// @TODO Update state
|
||||||
|
it = self.map.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
if (entry.value.state == .active) {
|
||||||
|
if (entry.value.neighbours >= 2 and entry.value.neighbours <= 3) {
|
||||||
|
entry.value.state = .active;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
entry.value.state = .inactive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (entry.value.neighbours == 3) {
|
||||||
|
entry.value.state = .active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_neighbour_states(self: *Simulation4D) void {
|
||||||
|
var it = self.map.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
var neighbours = entry.value.get_neighbour_positions(self.allocator);
|
||||||
|
//defer self.allocator.free(neighbours);
|
||||||
|
var n_active : u64 = 0;
|
||||||
|
//std.log.warn("Checking neighbours for ({}, {}, {})",
|
||||||
|
// .{entry.value.pos[0], entry.value.pos[1], entry.value.pos[2]});
|
||||||
|
for (neighbours) |n_pos| {
|
||||||
|
if (self.map.get(n_pos)) |n| {
|
||||||
|
if (n.state == .active) {
|
||||||
|
//std.log.warn("Neighbour at ({}, {}, {}) is active",
|
||||||
|
// .{n.pos[0], n.pos[1], n.pos[2]});
|
||||||
|
n_active += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Noop, another function creates our neighbours, and it should
|
||||||
|
// be run before this one.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry.value.neighbours = n_active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count_active_cubes(self: *Simulation4D) u64 {
|
||||||
|
var it = self.map.iterator();
|
||||||
|
var count : u64 = 0;
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
if (entry.value.state == .active) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test "get_neighbour_positions" {
|
||||||
|
var cube = Cube{
|
||||||
|
.pos = [_]i64{0, 0, 0},
|
||||||
|
};
|
||||||
|
var neighbours = cube.get_neighbour_positions();
|
||||||
|
for (neighbours) |n| {
|
||||||
|
std.log.warn("({}, {}, {})", .{n[0], n[1], n[2]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "small_cubeway" {
|
||||||
|
var sim = try Simulation.init(std.testing.allocator);
|
||||||
|
defer sim.deinit();
|
||||||
|
try sim.add_cube([_]i64 {0, 0, 0}, .inactive);
|
||||||
|
try sim.add_cube([_]i64 {1, 0, 0}, .active);
|
||||||
|
try sim.add_cube([_]i64 {2, 0, 0}, .inactive);
|
||||||
|
|
||||||
|
try sim.add_cube([_]i64 {0, 1, 0}, .inactive);
|
||||||
|
try sim.add_cube([_]i64 {1, 1, 0}, .inactive);
|
||||||
|
try sim.add_cube([_]i64 {2, 1, 0}, .active);
|
||||||
|
|
||||||
|
try sim.add_cube([_]i64 {0, 2, 0}, .active);
|
||||||
|
try sim.add_cube([_]i64 {1, 2, 0}, .active);
|
||||||
|
try sim.add_cube([_]i64 {2, 2, 0}, .active);
|
||||||
|
|
||||||
|
try sim.print_state();
|
||||||
|
sim.calculate_neighbour_states();
|
||||||
|
try sim.print_state_neighbour_count();
|
||||||
|
std.testing.expectEqual(@as(u64, 5), sim.count_active_cubes());
|
||||||
|
|
||||||
|
std.log.warn("\n\n --- First round --- \n", .{});
|
||||||
|
try sim.do_round(); // 1
|
||||||
|
try sim.print_state();
|
||||||
|
std.testing.expectEqual(@as(u64, 11), sim.count_active_cubes());
|
||||||
|
|
||||||
|
try sim.do_round(); // 2
|
||||||
|
try sim.do_round(); // 3
|
||||||
|
try sim.do_round(); // 4
|
||||||
|
try sim.do_round(); // 5
|
||||||
|
try sim.do_round(); // 6
|
||||||
|
|
||||||
|
var active = sim.count_active_cubes();
|
||||||
|
std.testing.expectEqual(@as(u64, 112), active);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "4d_cubeway" {
|
||||||
|
var sim = try Simulation4D.init(std.testing.allocator);
|
||||||
|
defer sim.deinit();
|
||||||
|
try sim.add_cube([_]i64 {0, 0, 0, 0}, .inactive);
|
||||||
|
try sim.add_cube([_]i64 {1, 0, 0, 0}, .active);
|
||||||
|
try sim.add_cube([_]i64 {2, 0, 0, 0}, .inactive);
|
||||||
|
|
||||||
|
try sim.add_cube([_]i64 {0, 1, 0, 0}, .inactive);
|
||||||
|
try sim.add_cube([_]i64 {1, 1, 0, 0}, .inactive);
|
||||||
|
try sim.add_cube([_]i64 {2, 1, 0, 0}, .active);
|
||||||
|
|
||||||
|
try sim.add_cube([_]i64 {0, 2, 0, 0}, .active);
|
||||||
|
try sim.add_cube([_]i64 {1, 2, 0, 0}, .active);
|
||||||
|
try sim.add_cube([_]i64 {2, 2, 0, 0}, .active);
|
||||||
|
|
||||||
|
try sim.do_round(); // 1
|
||||||
|
try sim.do_round(); // 2
|
||||||
|
try sim.do_round(); // 3
|
||||||
|
try sim.do_round(); // 4
|
||||||
|
try sim.do_round(); // 5
|
||||||
|
try sim.do_round(); // 6
|
||||||
|
|
||||||
|
var active = sim.count_active_cubes();
|
||||||
|
std.testing.expectEqual(@as(u64, 848), active);
|
||||||
|
}
|
Reference in New Issue