cem 0.1.3 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/cem.gemspec +5 -3
- data/examples/aoc2018/.gitignore +2 -0
- data/examples/aoc2018/day1.rb +13 -0
- data/examples/aoc2018/day10.rb +61 -0
- data/examples/aoc2018/day11.rb +66 -0
- data/examples/aoc2018/day12.rb +61 -0
- data/examples/aoc2018/day13.rb +167 -0
- data/examples/aoc2018/day14.rb +31 -0
- data/examples/aoc2018/day14_2.rb +71 -0
- data/examples/aoc2018/day15.rb +271 -0
- data/examples/aoc2018/day16.rb +158 -0
- data/examples/aoc2018/day17.rb +203 -0
- data/examples/aoc2018/day18.rb +113 -0
- data/examples/aoc2018/day18_rspec.rb +131 -0
- data/examples/aoc2018/day19.rb +145 -0
- data/examples/aoc2018/day1_part2.rb +30 -0
- data/examples/aoc2018/day2.rb +20 -0
- data/examples/aoc2018/day20.rb +103 -0
- data/examples/aoc2018/day21.rb +158 -0
- data/examples/aoc2018/day21_v2.rb +137 -0
- data/examples/aoc2018/day21_v3.rb +157 -0
- data/examples/aoc2018/day21_v4.rb +141 -0
- data/examples/aoc2018/day22.rb +212 -0
- data/examples/aoc2018/day23.rb +148 -0
- data/examples/aoc2018/day24.rb +156 -0
- data/examples/aoc2018/day25.rb +52 -0
- data/examples/aoc2018/day2_2.rb +27 -0
- data/examples/aoc2018/day3.rb +71 -0
- data/examples/aoc2018/day4.rb +46 -0
- data/examples/aoc2018/day5.rb +23 -0
- data/examples/aoc2018/day5_part2.rb +29 -0
- data/examples/aoc2018/day6.rb +53 -0
- data/examples/aoc2018/day7.rb +110 -0
- data/examples/aoc2018/day8.rb +39 -0
- data/examples/aoc2018/day8_part2.rb +65 -0
- data/examples/aoc2018/day9.rb +51 -0
- data/examples/aoc2018/day9_circular.rb +125 -0
- data/examples/aoc2018/inputs/day10_input.txt +395 -0
- data/examples/aoc2018/inputs/day11_input.txt +1 -0
- data/examples/aoc2018/inputs/day12_input.txt +34 -0
- data/examples/aoc2018/inputs/day12_input2.txt +16 -0
- data/examples/aoc2018/inputs/day13_input.txt +150 -0
- data/examples/aoc2018/inputs/day13_test.txt +6 -0
- data/examples/aoc2018/inputs/day14_input.txt +1 -0
- data/examples/aoc2018/inputs/day15_input.txt +32 -0
- data/examples/aoc2018/inputs/day15_input10.txt +32 -0
- data/examples/aoc2018/inputs/day15_input11.txt +32 -0
- data/examples/aoc2018/inputs/day15_input12.txt +32 -0
- data/examples/aoc2018/inputs/day15_input13.txt +32 -0
- data/examples/aoc2018/inputs/day15_input14.txt +32 -0
- data/examples/aoc2018/inputs/day15_input2.txt +7 -0
- data/examples/aoc2018/inputs/day15_input3.txt +9 -0
- data/examples/aoc2018/inputs/day15_input4.txt +7 -0
- data/examples/aoc2018/inputs/day15_input5.txt +7 -0
- data/examples/aoc2018/inputs/day15_input6.txt +7 -0
- data/examples/aoc2018/inputs/day15_input7.txt +7 -0
- data/examples/aoc2018/inputs/day15_input8.txt +5 -0
- data/examples/aoc2018/inputs/day15_input9.txt +7 -0
- data/examples/aoc2018/inputs/day15_test.txt +9 -0
- data/examples/aoc2018/inputs/day16_input.txt +3865 -0
- data/examples/aoc2018/inputs/day17_input.txt +2229 -0
- data/examples/aoc2018/inputs/day17_input_test.txt +8 -0
- data/examples/aoc2018/inputs/day18_input.txt +50 -0
- data/examples/aoc2018/inputs/day18_test.txt +10 -0
- data/examples/aoc2018/inputs/day19_input.txt +48 -0
- data/examples/aoc2018/inputs/day1_input.txt +955 -0
- data/examples/aoc2018/inputs/day20_input.txt +1 -0
- data/examples/aoc2018/inputs/day21_input.txt +32 -0
- data/examples/aoc2018/inputs/day22_input.txt +2 -0
- data/examples/aoc2018/inputs/day23_input.txt +1000 -0
- data/examples/aoc2018/inputs/day23_input2.txt +9 -0
- data/examples/aoc2018/inputs/day24_input.txt +24 -0
- data/examples/aoc2018/inputs/day25_input.txt +1483 -0
- data/examples/aoc2018/inputs/day2_input.txt +250 -0
- data/examples/aoc2018/inputs/day3_input.txt +1233 -0
- data/examples/aoc2018/inputs/day4_input.txt +1140 -0
- data/examples/aoc2018/inputs/day5_input.txt +1 -0
- data/examples/aoc2018/inputs/day6_input.txt +50 -0
- data/examples/aoc2018/inputs/day7_input.txt +101 -0
- data/examples/aoc2018/inputs/day8_input.txt +1 -0
- data/examples/aoc2018/inputs/day9_input.txt +1 -0
- data/lib/cem/ccommon.rb +67 -12
- data/lib/cem/cflame/popen.rb +89 -0
- data/lib/cem/cflame.rb +2 -0
- data/lib/cem/cruzzles.rb +198 -50
- data/lib/cem/version.rb +1 -1
- data/lib/cem.rb +0 -6
- metadata +91 -11
@@ -0,0 +1,148 @@
|
|
1
|
+
#
|
2
|
+
# https://adventofcode.com/2018/day/23 part 1 and part 2
|
3
|
+
#
|
4
|
+
# Uses Point3D. I probably need to revisit how to subclass Point3D, Point2D.
|
5
|
+
#
|
6
|
+
require 'cem'
|
7
|
+
|
8
|
+
Bot = Struct.new("Bot", :x, :y, :z, :r) {
|
9
|
+
|
10
|
+
def [](i)
|
11
|
+
case i
|
12
|
+
when 0
|
13
|
+
x
|
14
|
+
when 1
|
15
|
+
y
|
16
|
+
when 2
|
17
|
+
z
|
18
|
+
when 3
|
19
|
+
r
|
20
|
+
else
|
21
|
+
raise
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
}
|
26
|
+
|
27
|
+
def manhattan(other, bot)
|
28
|
+
(other.z - bot.z).abs + (other.y - bot.y).abs + (other.x - bot.x).abs
|
29
|
+
end
|
30
|
+
|
31
|
+
def manhattan_box(bot, min, max)
|
32
|
+
|
33
|
+
result = (0..2).sum { |i|
|
34
|
+
if bot[i] >= min[i] && bot[i] <= max[i]
|
35
|
+
0
|
36
|
+
elsif bot[i] < min[i]
|
37
|
+
min[i] - bot[i]
|
38
|
+
else
|
39
|
+
bot[i] - max[i]
|
40
|
+
end
|
41
|
+
}
|
42
|
+
|
43
|
+
#puts "#{bot.inspect} - #{min} - #{max} - #{result}"
|
44
|
+
return result
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
lines = File.readlines("inputs/day23_input.txt", chomp: true)
|
49
|
+
|
50
|
+
input = []
|
51
|
+
|
52
|
+
lines.each { |line|
|
53
|
+
|
54
|
+
if line =~ /^pos=<([+-]?\d+),([+-]?\d+),([+-]?\d+)>, r=([+-]?\d+)$/
|
55
|
+
v1 = $1.to_i
|
56
|
+
v2 = $2.to_i
|
57
|
+
v3 = $3.to_i
|
58
|
+
v4 = $4.to_i
|
59
|
+
|
60
|
+
input << Bot.new(v1,v2,v3,v4)
|
61
|
+
end
|
62
|
+
|
63
|
+
}
|
64
|
+
|
65
|
+
#
|
66
|
+
# Part I
|
67
|
+
#
|
68
|
+
bot = input.max_by { |bot| bot.r }
|
69
|
+
|
70
|
+
strength = 0
|
71
|
+
input.each { |other|
|
72
|
+
|
73
|
+
if manhattan(bot, other) <= bot.r
|
74
|
+
strength += 1
|
75
|
+
end
|
76
|
+
}
|
77
|
+
|
78
|
+
puts "Part I: #{strength}"
|
79
|
+
|
80
|
+
#
|
81
|
+
# Part II
|
82
|
+
#
|
83
|
+
|
84
|
+
# Start with the largest group we can trivially find
|
85
|
+
best = []
|
86
|
+
bestScore = -1
|
87
|
+
|
88
|
+
min = Point3D.new(*[0,1,2].map { |i| input.map { |b| b[i] }.min })
|
89
|
+
max = Point3D.new(*[0,1,2].map { |i| input.map { |b| b[i] }.max })
|
90
|
+
|
91
|
+
todo = []
|
92
|
+
|
93
|
+
# todo == [ queue of [Box min, Box max, number of elements fully covering box, number of elements still under considerations] ]
|
94
|
+
todo << [min, max, 0, input]
|
95
|
+
|
96
|
+
i = 0
|
97
|
+
while todo.size > 0
|
98
|
+
|
99
|
+
mi, ma, covered, bots = todo.shift
|
100
|
+
|
101
|
+
puts "Iteration #{i} - Still todo: #{todo.size} - Cover: #{covered} Bots: #{bots.size} Cur Size: #{(ma-mi).manhattan} - BestScore: #{bestScore}" if i % 128 == 0
|
102
|
+
i += 1
|
103
|
+
|
104
|
+
if mi == ma
|
105
|
+
hit, miss = bots.partition { |bot| manhattan(bot, mi) <= bot.r }
|
106
|
+
|
107
|
+
if bestScore < covered + hit.size
|
108
|
+
bestScore = covered + hit.size
|
109
|
+
best = [mi]
|
110
|
+
elsif bestScore == covered + hit.size
|
111
|
+
best << mi
|
112
|
+
end
|
113
|
+
|
114
|
+
next
|
115
|
+
end
|
116
|
+
|
117
|
+
# Which bots do touch the current min-max box
|
118
|
+
hit, miss = bots.partition { |bot| manhattan_box(bot, mi, ma) <= bot.r }
|
119
|
+
next if covered + hit.size < bestScore # We don't have to dig in, if we have a better high score anyway
|
120
|
+
|
121
|
+
# Which bots do completely cover the entire box and which don't? Check 8 corners to find out
|
122
|
+
corners = (0..7).map { |i| Point3D.new(i & 4 == 0 ? mi.x : ma.x, i & 2 == 0 ? mi.y : ma.y, i & 1 == 0 ? mi.z : ma.z) }
|
123
|
+
total, partial = hit.partition { |bot| corners.count { |c| manhattan(bot, c) <= bot.r } == 8 }
|
124
|
+
|
125
|
+
# Split the largest dimension of the box in half (mi to midI, midA to ma)
|
126
|
+
minDim = [0,1,2].max_by { |i| ma[i] - mi[i] }
|
127
|
+
|
128
|
+
midI = ma.clone
|
129
|
+
midI[minDim] = mi[minDim] + (ma[minDim] - mi[minDim]) / 2
|
130
|
+
|
131
|
+
midA = mi.clone
|
132
|
+
midA[minDim] = mi[minDim] + (ma[minDim] - mi[minDim]) / 2 + 1
|
133
|
+
|
134
|
+
todo << [mi, midI, covered + total.size, partial]
|
135
|
+
todo << [midA, ma, covered + total.size, partial]
|
136
|
+
|
137
|
+
todo.sort_by! { |t| -(t[2] + t[3].size) * (max-min).manhattan + manhattan(t[0], t[1]) } if i % 4 == 0
|
138
|
+
end
|
139
|
+
|
140
|
+
puts "Best Positions: " + best.to_s
|
141
|
+
puts "Number of Bots In Range: #{bestScore}"
|
142
|
+
puts "Lowest Manhattan Distance to 0,0,0: #{best.map { |b| b.manhattan }.min}"
|
143
|
+
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
|
148
|
+
|
@@ -0,0 +1,156 @@
|
|
1
|
+
#
|
2
|
+
# https://adventofcode.com/2018/day/24 part 1 and 2
|
3
|
+
#
|
4
|
+
# Does not use any cem functions
|
5
|
+
#
|
6
|
+
|
7
|
+
Group = Struct.new("Group", :immuneSystem, :units, :hitPoints, :weakness, :immunities, :damage, :damageType, :initiative) {
|
8
|
+
|
9
|
+
def damageCalc(other)
|
10
|
+
damage * units * (other.immunities.include?(damageType) ? 0 : 1) * (other.weakness.include?(damageType) ? 2 : 1)
|
11
|
+
end
|
12
|
+
|
13
|
+
def effectivePow
|
14
|
+
damage * units
|
15
|
+
end
|
16
|
+
}
|
17
|
+
|
18
|
+
lines = File.readlines("inputs/day24_input.txt")
|
19
|
+
|
20
|
+
groups = []
|
21
|
+
immuneSystem = false
|
22
|
+
|
23
|
+
lines.each { |line|
|
24
|
+
line.strip!
|
25
|
+
|
26
|
+
if line =~ /^Immune System:$/
|
27
|
+
immuneSystem = true
|
28
|
+
elsif line =~ /^Infection:$/
|
29
|
+
immuneSystem = false
|
30
|
+
elsif line =~ /^([+-]?\d+) units each with ([+-]?\d+) hit points\s*(.*?) with an attack that does ([+-]?\d+) (\w+) damage at initiative ([+-]?\d+)$/
|
31
|
+
units = $1.to_i
|
32
|
+
hitPoints = $2.to_i
|
33
|
+
immunities = []
|
34
|
+
weakness = []
|
35
|
+
w = $3
|
36
|
+
damage = $4.to_i
|
37
|
+
damageType = $5
|
38
|
+
initiative = $6.to_i
|
39
|
+
|
40
|
+
w.gsub(/\(|\)/, "").split("; ").each { |s|
|
41
|
+
if s =~ /immune to (.*)/
|
42
|
+
immunities = $1.split(", ")
|
43
|
+
end
|
44
|
+
if s =~ /weak to (.*)/
|
45
|
+
weakness = $1.split(", ")
|
46
|
+
end
|
47
|
+
}
|
48
|
+
|
49
|
+
g = Group.new(immuneSystem, units, hitPoints, weakness, immunities, damage, damageType, initiative)
|
50
|
+
groups << g
|
51
|
+
# puts g.inspect
|
52
|
+
elsif line == ""
|
53
|
+
|
54
|
+
else
|
55
|
+
raise line.inspect
|
56
|
+
end
|
57
|
+
}
|
58
|
+
|
59
|
+
def run(groups)
|
60
|
+
|
61
|
+
max_init = groups.map { |g| g.initiative }.max + 1
|
62
|
+
|
63
|
+
@max_init = max_init
|
64
|
+
|
65
|
+
round = 0
|
66
|
+
|
67
|
+
while groups.count { |g| g.immuneSystem } != 0 && groups.count { |g| !g.immuneSystem } != 0
|
68
|
+
|
69
|
+
anyDamage = false
|
70
|
+
|
71
|
+
groups.sort_by! { |g| [-g.damage * g.units, -g.initiative] }
|
72
|
+
|
73
|
+
# Target Selection
|
74
|
+
attackedGroups = []
|
75
|
+
attackPairs = []
|
76
|
+
groups.each { |g|
|
77
|
+
others = groups.select { |other| g.immuneSystem != other.immuneSystem && !attackedGroups.include?(other) }
|
78
|
+
|
79
|
+
groupToAttack = others.sort_by { |other| [-g.damageCalc(other), -other.damage * other.units, -other.initiative] }.first
|
80
|
+
|
81
|
+
if groupToAttack && g.damageCalc(groupToAttack) != 0
|
82
|
+
attackedGroups << groupToAttack
|
83
|
+
attackPairs << [g, groupToAttack]
|
84
|
+
end
|
85
|
+
}
|
86
|
+
|
87
|
+
attackPairs.sort_by! { |x| -x[0].initiative }
|
88
|
+
|
89
|
+
# Attack phase
|
90
|
+
attackPairs.each { |x|
|
91
|
+
from, to = x
|
92
|
+
damage = from.damageCalc(to)
|
93
|
+
|
94
|
+
killedUnits = damage / to.hitPoints
|
95
|
+
if killedUnits > 0
|
96
|
+
anyDamage = true
|
97
|
+
end
|
98
|
+
to.units -= killedUnits
|
99
|
+
|
100
|
+
if to.units <= 0
|
101
|
+
to.units = 0
|
102
|
+
groups = groups - [to]
|
103
|
+
end
|
104
|
+
}
|
105
|
+
|
106
|
+
groups.sort_by! { |g| -(g.immuneSystem ? 1 : 0) * 100000000000 - (g.damage * g.units * max_init + g.initiative)}
|
107
|
+
|
108
|
+
# puts round
|
109
|
+
round += 1
|
110
|
+
# system("cls") if round % 1024 == 0
|
111
|
+
# groups.each { |g| puts g.inspect } if round % 1024 == 0
|
112
|
+
|
113
|
+
if !anyDamage
|
114
|
+
groups.each { |g| puts g.inspect }
|
115
|
+
return
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
groups
|
120
|
+
end
|
121
|
+
|
122
|
+
tempGroup = groups.map { |g| g.dup }
|
123
|
+
|
124
|
+
puts "Part 1: #{run(tempGroup).sum{|g| g.units }}"
|
125
|
+
|
126
|
+
def part2(groups)
|
127
|
+
|
128
|
+
minAdd = 1
|
129
|
+
|
130
|
+
loop do
|
131
|
+
|
132
|
+
tempGroup = groups.map { |g| g.dup }
|
133
|
+
|
134
|
+
tempGroup.each { |g|
|
135
|
+
if g.immuneSystem
|
136
|
+
g.damage += minAdd
|
137
|
+
end
|
138
|
+
}
|
139
|
+
|
140
|
+
res = run(tempGroup)
|
141
|
+
|
142
|
+
if res == nil
|
143
|
+
puts "Boost: #{minAdd} - Got stuck"
|
144
|
+
else
|
145
|
+
puts "Boost: #{minAdd} - #{res[0].immuneSystem}"
|
146
|
+
|
147
|
+
if res[0].immuneSystem
|
148
|
+
return res
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
minAdd += 1
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
puts "Part 2: #{part2(groups).sum { |g| g.units }}"
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# https://adventofcode.com/2018/day/25 part 1 (there is only 1
|
4
|
+
#
|
5
|
+
# Uses Point4D, but not much to see here.
|
6
|
+
#
|
7
|
+
require 'cem'
|
8
|
+
|
9
|
+
lines = File.readlines("inputs/day25_input.txt")
|
10
|
+
|
11
|
+
input = []
|
12
|
+
|
13
|
+
lines.each { |line|
|
14
|
+
|
15
|
+
if line =~ /^\s*([+-]?\d+),([+-]?\d+),([+-]?\d+),([+-]?\d+)\s*$/
|
16
|
+
v1 = $1.to_i
|
17
|
+
v2 = $2.to_i
|
18
|
+
v3 = $3.to_i
|
19
|
+
v4 = $4.to_i
|
20
|
+
input << Point4D.new(v1,v2,v3,v4)
|
21
|
+
else
|
22
|
+
raise line.inspect
|
23
|
+
end
|
24
|
+
}
|
25
|
+
|
26
|
+
constellations = []
|
27
|
+
|
28
|
+
input.each { |p|
|
29
|
+
if c = constellations.find { |c| c.any? { |p2| p2.manhattan(p) <= 3 } }
|
30
|
+
c << p
|
31
|
+
else
|
32
|
+
constellations << [p]
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
36
|
+
loop do
|
37
|
+
count = constellations.count { |c| c.size > 0 }
|
38
|
+
|
39
|
+
constellations.each_with_index { |c,i|
|
40
|
+
constellations.each_with_index { |d,j|
|
41
|
+
next if j <= i
|
42
|
+
if c.any? { |p| d.any? { |p2| p2.manhattan(p) <= 3 } }
|
43
|
+
c.push(*d)
|
44
|
+
d.clear
|
45
|
+
end
|
46
|
+
}
|
47
|
+
}
|
48
|
+
break if count == constellations.count { |c| c.size > 0 }
|
49
|
+
end
|
50
|
+
|
51
|
+
puts "Part 1: #{constellations.count { |c| c.size > 0 }}"
|
52
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# https://adventofcode.com/2018/day/2 part 2
|
4
|
+
#
|
5
|
+
# Does not use any cem functions \o/
|
6
|
+
|
7
|
+
previous = ''
|
8
|
+
File.readlines("inputs/day2_input.txt").sort.each { |l|
|
9
|
+
|
10
|
+
mismatch = 0
|
11
|
+
s = ''
|
12
|
+
if previous != ''
|
13
|
+
l.split("").each_with_index { |c, i|
|
14
|
+
if previous[i] != c
|
15
|
+
mismatch += 1
|
16
|
+
else
|
17
|
+
s += c
|
18
|
+
end
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
if mismatch == 1
|
23
|
+
puts s
|
24
|
+
end
|
25
|
+
|
26
|
+
previous = l
|
27
|
+
}
|
@@ -0,0 +1,71 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# https://adventofcode.com/2018/day/3 part 1 and 2
|
4
|
+
#
|
5
|
+
# Demonstrates: Point2D, min(a,b), Array::with_progress
|
6
|
+
#
|
7
|
+
# Areas for improvement: Rect intersection
|
8
|
+
|
9
|
+
require 'set'
|
10
|
+
require 'cem'
|
11
|
+
|
12
|
+
claims = []
|
13
|
+
intersections = Set.new
|
14
|
+
doesIntersect = Set.new
|
15
|
+
|
16
|
+
Claim = Struct.new("Claim", :x, :y, :width, :height, :claimId)
|
17
|
+
|
18
|
+
File.readlines("inputs/day3_input.txt").with_progress.each { |l|
|
19
|
+
|
20
|
+
if l =~ /^\#(\d+) \@ (\d+),(\d+)\: (\d+)x(\d+)$/
|
21
|
+
|
22
|
+
claim = Claim.new($2.to_i, $3.to_i, $4.to_i, $5.to_i, $1.to_i)
|
23
|
+
|
24
|
+
claims.each { |other|
|
25
|
+
|
26
|
+
x = claim.x
|
27
|
+
y = claim.y
|
28
|
+
w = claim.width
|
29
|
+
h = claim.height
|
30
|
+
|
31
|
+
x2 = other.x
|
32
|
+
y2 = other.y
|
33
|
+
w2 = other.width
|
34
|
+
h2 = other.height
|
35
|
+
|
36
|
+
if x2 < x
|
37
|
+
x, x2 = x2, x
|
38
|
+
w, w2 = w2, w
|
39
|
+
end
|
40
|
+
|
41
|
+
if y2 < y
|
42
|
+
y, y2 = y2, y
|
43
|
+
h, h2 = h2, h
|
44
|
+
end
|
45
|
+
|
46
|
+
# puts "#{claim} - #{other}"
|
47
|
+
|
48
|
+
if x + w >= x2 && y + h >= y2
|
49
|
+
|
50
|
+
width = min(x2 + w2, x + w)
|
51
|
+
height = min(y2 + h2, y + h)
|
52
|
+
|
53
|
+
doesIntersect.add(other)
|
54
|
+
doesIntersect.add(claim)
|
55
|
+
|
56
|
+
for a in x2...width
|
57
|
+
for b in y2...height
|
58
|
+
# puts "#{a} #{b}"
|
59
|
+
intersections.add(Point2D.new(a,b))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
}
|
65
|
+
|
66
|
+
claims << claim
|
67
|
+
end
|
68
|
+
}
|
69
|
+
|
70
|
+
puts "Part 1: #{intersections.size}"
|
71
|
+
puts "Part 2: #{claims.to_set.subtract(doesIntersect).first.claimId}"
|
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# https://adventofcode.com/2018/day/4
|
4
|
+
#
|
5
|
+
# Does not use any cem functions \o/
|
6
|
+
|
7
|
+
Guard = Struct.new("Guard", :id, :total, :minutes)
|
8
|
+
|
9
|
+
guards = {}
|
10
|
+
guard = nil
|
11
|
+
asleep = nil
|
12
|
+
|
13
|
+
File.readlines("inputs/day4_input.txt", chomp: true).sort.each { |l|
|
14
|
+
|
15
|
+
if l =~ /Guard \#(\d+) begins shift$/
|
16
|
+
|
17
|
+
id = $1.to_i
|
18
|
+
guard = guards[id] ||= Guard.new(id, 0, [0] * 60)
|
19
|
+
|
20
|
+
elsif / 00:(?<minute>\d+)\] falls asleep$/ =~ l
|
21
|
+
|
22
|
+
asleep = minute.to_i
|
23
|
+
|
24
|
+
elsif l =~ / 00:(\d+)\] wakes up$/
|
25
|
+
|
26
|
+
awake = $1.to_i
|
27
|
+
|
28
|
+
guard.total += awake - asleep
|
29
|
+
(asleep...awake).each { |min|
|
30
|
+
guard.minutes[min] += 1
|
31
|
+
}
|
32
|
+
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
36
|
+
guard = (guards.values.sort_by {|g| g.total}).last
|
37
|
+
puts "Part1: #{guard.id * guard.minutes.each_with_index.max[1]}"
|
38
|
+
|
39
|
+
guard = (guards.values.sort_by {|g| g.minutes.each_with_index.max[0] }).last
|
40
|
+
puts "Part2: #{guard.id * guard.minutes.each_with_index.max[1]}"
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# https://adventofcode.com/2018/day/5 Part 1
|
4
|
+
#
|
5
|
+
# Does not use any cem functions \o/ but did you know:
|
6
|
+
#
|
7
|
+
# - String#swapcase turns "Hello" into "hELLO"?
|
8
|
+
#
|
9
|
+
|
10
|
+
out = []
|
11
|
+
|
12
|
+
File.read("inputs/day5_input.txt").each_char { |c|
|
13
|
+
|
14
|
+
if out.empty? || out.last != c.swapcase
|
15
|
+
out << c
|
16
|
+
else
|
17
|
+
out.pop
|
18
|
+
end
|
19
|
+
|
20
|
+
}
|
21
|
+
|
22
|
+
puts out.join.size
|
23
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# https://adventofcode.com/2018/day/5 Part 2
|
4
|
+
#
|
5
|
+
# Does not use any cem functions \o/ but did you know:
|
6
|
+
#
|
7
|
+
# - String#swapcase turns a Hello into hELLO?
|
8
|
+
# - Regex support 'i' for insensitive matches?
|
9
|
+
#
|
10
|
+
|
11
|
+
File.readlines("inputs/day5_input.txt").each { |line|
|
12
|
+
|
13
|
+
puts [*'a'..'z'].map { |delete|
|
14
|
+
|
15
|
+
out = []
|
16
|
+
|
17
|
+
line.gsub(/#{delete}/i, '').each_char { |c|
|
18
|
+
if out.empty? || out.last != c.swapcase
|
19
|
+
out << c
|
20
|
+
else
|
21
|
+
out.pop
|
22
|
+
end
|
23
|
+
}
|
24
|
+
|
25
|
+
out.join.size
|
26
|
+
}.min
|
27
|
+
}
|
28
|
+
|
29
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# https://adventofcode.com/2018/day/6 Part 1 and 2
|
4
|
+
#
|
5
|
+
# Uses Point2D from cem
|
6
|
+
|
7
|
+
require 'cem'
|
8
|
+
|
9
|
+
out = {}
|
10
|
+
points = []
|
11
|
+
largestRegion = 0
|
12
|
+
|
13
|
+
File.readlines("inputs/day6_input.txt", chomp: true).each { |line|
|
14
|
+
if line =~ /^(\d+), (\d+)$/
|
15
|
+
v1 = $1.to_i
|
16
|
+
v2 = $2.to_i
|
17
|
+
|
18
|
+
points << Point2D.new(v1, v2)
|
19
|
+
end
|
20
|
+
}
|
21
|
+
|
22
|
+
minX, maxX = points.map { |p| p.x }.minmax
|
23
|
+
minY, maxY = points.map { |p| p.y }.minmax
|
24
|
+
|
25
|
+
(minX..maxX).each { |x|
|
26
|
+
(minY..maxY).each { |y|
|
27
|
+
|
28
|
+
coord = Point2D.new(x,y)
|
29
|
+
|
30
|
+
nearest = points.group_by { |p| p.manhattan(coord) }.min_by { |k,v| k }
|
31
|
+
|
32
|
+
# puts nearest.inspect
|
33
|
+
if nearest.last.size == 1
|
34
|
+
(out[nearest.last.first] ||= []) << coord
|
35
|
+
end
|
36
|
+
|
37
|
+
if points.sum { |p| p.manhattan(coord) } < 10000
|
38
|
+
largestRegion += 1
|
39
|
+
end
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
puts "Part 1"
|
44
|
+
puts out.max_by { |k,v|
|
45
|
+
if v.select { |coord| (coord.x == maxX || coord.x == minX || coord.y == maxY || coord.y == minY) }.empty?
|
46
|
+
v.size
|
47
|
+
else
|
48
|
+
0 # don't care about infinite
|
49
|
+
end
|
50
|
+
}.last.size
|
51
|
+
|
52
|
+
puts "Part 2"
|
53
|
+
puts largestRegion
|
@@ -0,0 +1,110 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# https://adventofcode.com/2018/day/7 part 1 and 2
|
4
|
+
#
|
5
|
+
# Does not use any cem functions \o/
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'set'
|
9
|
+
|
10
|
+
Worker = Struct.new("Worker", :readyAt, :workingOn)
|
11
|
+
|
12
|
+
def read
|
13
|
+
input = File.readlines("inputs/day7_input.txt", chomp: true)
|
14
|
+
|
15
|
+
@edges = {}
|
16
|
+
@chars = Set.new
|
17
|
+
|
18
|
+
input.each { |line|
|
19
|
+
|
20
|
+
if line =~ /^Step (.+) must be finished before step (.+) can begin.$/
|
21
|
+
to = $1
|
22
|
+
from = $2
|
23
|
+
|
24
|
+
@chars.add(to)
|
25
|
+
@chars.add(from)
|
26
|
+
|
27
|
+
(@edges[from] ||= []) << to
|
28
|
+
end
|
29
|
+
|
30
|
+
@chars.each { |c| @edges[c] ||= [] }
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def anyWorkerReady
|
35
|
+
return @idleWorkers.size > 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def availableWork
|
39
|
+
return @chars.select { |c| @edges.include?(c) && @edges[c].size == 0 }.sort
|
40
|
+
end
|
41
|
+
|
42
|
+
def anyWorkAvailable
|
43
|
+
return availableWork.size > 0
|
44
|
+
end
|
45
|
+
|
46
|
+
def timeStep
|
47
|
+
|
48
|
+
puts "#{@timeNow}: #{@busyWorkers.inspect}" if $DEBUG
|
49
|
+
|
50
|
+
w = @busyWorkers.min_by { |w| w.readyAt }
|
51
|
+
@timeNow = w.readyAt
|
52
|
+
@busyWorkers -= [w]
|
53
|
+
@idleWorkers += [w]
|
54
|
+
|
55
|
+
c = w.workingOn
|
56
|
+
|
57
|
+
@result += c
|
58
|
+
|
59
|
+
@edges.update(@edges) { |k,v|
|
60
|
+
v - [c]
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def scheduleWork
|
65
|
+
|
66
|
+
nextTask = availableWork.first
|
67
|
+
|
68
|
+
w = @idleWorkers.pop
|
69
|
+
@busyWorkers += [w]
|
70
|
+
w.workingOn = nextTask
|
71
|
+
w.readyAt = @timeNow + 61 + nextTask.ord - 'A'.ord
|
72
|
+
@edges.delete(nextTask)
|
73
|
+
|
74
|
+
puts "Scheduling #{nextTask} to be ready at #{w.readyAt}" if $DEBUG
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
def process(parallelism)
|
79
|
+
|
80
|
+
read()
|
81
|
+
|
82
|
+
@timeNow = 0
|
83
|
+
@result = ""
|
84
|
+
|
85
|
+
@busyWorkers = []
|
86
|
+
@idleWorkers = []
|
87
|
+
parallelism.times { @idleWorkers << Worker.new(0, '') }
|
88
|
+
|
89
|
+
loop do
|
90
|
+
|
91
|
+
while (!anyWorkerReady || !anyWorkAvailable) && @busyWorkers.size > 0
|
92
|
+
timeStep
|
93
|
+
end
|
94
|
+
|
95
|
+
if !anyWorkAvailable
|
96
|
+
break
|
97
|
+
end
|
98
|
+
|
99
|
+
scheduleWork
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
puts "Parallelism: #{parallelism}"
|
104
|
+
puts @result
|
105
|
+
puts @timeNow
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
process(1)
|
110
|
+
process(5)
|