1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
const std = @import("std");
const aoc = @import("aoc");
const Color = enum { BLACK, WHITE };
const Dir = enum { E, SE, SW, W, NW, NE };
const dirs = [_]aoc.Pos{
aoc.Pos{ .x = 2, .y = 0 },
aoc.Pos{ .x = 1, .y = -1 },
aoc.Pos{ .x = -1, .y = -1 },
aoc.Pos{ .x = -2, .y = 0 },
aoc.Pos{ .x = -1, .y = 1 },
aoc.Pos{ .x = 1, .y = 1 },
};
const tokens = [_][]const u8{
"e",
"se",
"sw",
"w",
"nw",
"ne",
};
const Tile = struct {
color: Color,
};
fn parseInput(tiles: *std.AutoHashMap(aoc.Pos, Tile), input: []const u8) !void {
var lineit = std.mem.tokenize(u8, input, "\n");
while (lineit.next()) |line| {
var pos = aoc.Pos{ .x = 0, .y = 0 };
var i: usize = 0;
while (i < line.len) {
var dir = for (tokens) |tok, ti| {
if (i + tok.len > line.len) continue;
if (std.mem.eql(u8, tok, line[i .. i + tok.len])) {
i += tok.len;
break @intToEnum(Dir, @intCast(u3, ti));
}
} else return aoc.Error.InvalidInput;
if (aoc.debug) std.debug.print("{} ", .{dir});
pos = pos.add(dirs[@enumToInt(dir)]);
}
if (aoc.debug) std.debug.print("=> {} {}\n", .{ pos.x, pos.y });
var tile = tiles.getEntry(pos);
if (tile != null) {
tile.?.value_ptr.color = if (tile.?.value_ptr.color == Color.WHITE) Color.BLACK else Color.WHITE;
} else {
try tiles.put(pos, Tile{ .color = Color.BLACK });
}
}
}
fn applyRule(pos: aoc.Pos, before: *std.AutoHashMap(aoc.Pos, Tile), after: *std.AutoHashMap(aoc.Pos, Tile)) !void {
if (after.contains(pos)) return;
const old_tile = before.get(pos);
const old_color = if (old_tile == null) Color.WHITE else old_tile.?.color;
var adj: u32 = 0;
for (dirs) |d| {
const tile = before.get(pos.add(d));
if (tile != null and tile.?.color == Color.BLACK) adj += 1;
}
if (adj == 2 and old_color == Color.WHITE) {
try after.put(pos, Tile{ .color = Color.BLACK });
} else if ((adj == 0 or adj > 2) and old_color == Color.BLACK) {
try after.put(pos, Tile{ .color = Color.WHITE });
} else {
try after.put(pos, Tile{ .color = old_color });
}
}
fn doRound(allocator: std.mem.Allocator, tiles: *std.AutoHashMap(aoc.Pos, Tile)) !void {
var result = std.AutoHashMap(aoc.Pos, Tile).init(allocator);
defer result.deinit();
var mapit = tiles.iterator();
while (mapit.next()) |kv| {
for (dirs) |d| {
try applyRule(kv.key_ptr.add(d), tiles, &result);
}
try applyRule(kv.key_ptr.*, tiles, &result);
}
std.mem.swap(std.AutoHashMap(aoc.Pos, Tile), &result, tiles);
}
fn countBlack(tiles: *std.AutoHashMap(aoc.Pos, Tile)) u32 {
var count: u32 = 0;
var mapit = tiles.iterator();
while (mapit.next()) |kv| {
if (kv.value_ptr.color == Color.BLACK) count += 1;
}
return count;
}
fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 {
_ = args;
var tiles = std.AutoHashMap(aoc.Pos, Tile).init(allocator);
defer tiles.deinit();
try parseInput(&tiles, input);
return try std.fmt.allocPrint(allocator, "{}", .{countBlack(&tiles)});
}
fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 {
_ = args;
var tiles = std.AutoHashMap(aoc.Pos, Tile).init(allocator);
defer tiles.deinit();
try parseInput(&tiles, input);
var round: usize = 0;
while (round < 100) : (round += 1) {
try doRound(allocator, &tiles);
}
return try std.fmt.allocPrint(allocator, "{}", .{countBlack(&tiles)});
}
pub const main = aoc.main(part1, part2, .{ "317", "3804" });
|