-
Notifications
You must be signed in to change notification settings - Fork 1
/
day_23.lua
142 lines (125 loc) · 3.35 KB
/
day_23.lua
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
local eio = require "libs.eio"
local profile = require "libs.profile"
local Vec2 = require "libs.Vec2"
local Set = require "libs.Set"
local MultiSet = require "libs.MultiSet"
local printf = eio.printf
local len = string.len
local sub = string.sub
local huge = math.huge
local P = Vec2.makeVec
local makeSet = Set.new
local makeMultiSet = MultiSet.new
local add = Set.add
local addm = MultiSet.add
profile.start()
local N = P(0, -1)
local S = P(0, 1)
local E = P(1, 0)
local W = P(-1, 0)
local NE = N + E
local NW = N + W
local SE = S + E
local SW = S + W
local DIRS = {N, S, W, E}
local ALL_DIRS = {N, NE, E, SE, S, SW, W, NW}
local CHECK_DIRS = {
[N] = {NE, N, NW},
[S] = {SE, S, SW},
[E] = {NE, E, SE},
[W] = {NW, W, SW}
}
local function parseInput ()
local positions = makeSet()
local y = 1
for line in io.lines() do
for x = 1, len(line) do
if sub(line, x, x) == "#" then
add(positions, P(x, y))
end
end
y = y + 1
end
return positions
end
local function hasElf (positions, pos, dir)
for _, checkDir in ipairs(CHECK_DIRS[dir]) do
if positions[pos + checkDir] then
return true
end
end
return false
end
local function hasAnyElfAround (positions, pos)
for _, dir in ipairs(ALL_DIRS) do
if positions[pos + dir] then
return true
end
end
return false
end
local function round (positions, i)
-- first half
local propositions = makeMultiSet{}
local nextPositions = {}
for pos in pairs(positions) do
if hasAnyElfAround(positions, pos) then
for j = 0, 3 do
local dir = DIRS[(i + j - 1) % #DIRS + 1]
if not hasElf(positions, pos, dir) then
local nextPos = pos + dir
nextPositions[pos] = nextPos
addm(propositions, nextPos, 1)
break
end
end
end
end
-- second half
local retPositions = makeSet()
local moved = false
for pos in pairs(positions) do
local nextPos = nextPositions[pos]
if nextPos and propositions[nextPos] == 1 then
moved = true
add(retPositions, nextPos)
else
add(retPositions, pos)
end
end
return retPositions, moved
end
local function findBounds (positions)
local minx = huge
local maxx = -huge
local miny = huge
local maxy = -huge
for pos in pairs(positions) do
if pos[1] > maxx then maxx = pos[1] end
if pos[1] < minx then minx = pos[1] end
if pos[2] > maxy then maxy = pos[2] end
if pos[2] < miny then miny = pos[2] end
end
return minx, maxx, miny, maxy
end
local function emptyGrounds (positions)
local minx, maxx, miny, maxy = findBounds(positions)
local w, h = maxx - minx + 1, maxy - miny + 1
return w * h - #positions
end
local function run (positions, n)
local moved
for i = 1, n do
positions, moved = round(positions, i)
if not moved then
return positions, i
end
end
return positions
end
local positions = parseInput()
local answer1 = emptyGrounds(run(positions, 10))
printf("Part 1: %i\n", answer1)
local _, answer2 = run(positions, huge)
printf("Part 2: %i\n", answer2)
return answer1, answer2, profile.finish()