cem 0.1.3 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/cem.gemspec +5 -3
  4. data/examples/aoc2018/.gitignore +2 -0
  5. data/examples/aoc2018/day1.rb +13 -0
  6. data/examples/aoc2018/day10.rb +61 -0
  7. data/examples/aoc2018/day11.rb +66 -0
  8. data/examples/aoc2018/day12.rb +61 -0
  9. data/examples/aoc2018/day13.rb +167 -0
  10. data/examples/aoc2018/day14.rb +31 -0
  11. data/examples/aoc2018/day14_2.rb +71 -0
  12. data/examples/aoc2018/day15.rb +271 -0
  13. data/examples/aoc2018/day16.rb +158 -0
  14. data/examples/aoc2018/day17.rb +203 -0
  15. data/examples/aoc2018/day18.rb +113 -0
  16. data/examples/aoc2018/day18_rspec.rb +131 -0
  17. data/examples/aoc2018/day19.rb +145 -0
  18. data/examples/aoc2018/day1_part2.rb +30 -0
  19. data/examples/aoc2018/day2.rb +20 -0
  20. data/examples/aoc2018/day20.rb +103 -0
  21. data/examples/aoc2018/day21.rb +158 -0
  22. data/examples/aoc2018/day21_v2.rb +137 -0
  23. data/examples/aoc2018/day21_v3.rb +157 -0
  24. data/examples/aoc2018/day21_v4.rb +141 -0
  25. data/examples/aoc2018/day22.rb +212 -0
  26. data/examples/aoc2018/day23.rb +148 -0
  27. data/examples/aoc2018/day24.rb +156 -0
  28. data/examples/aoc2018/day25.rb +52 -0
  29. data/examples/aoc2018/day2_2.rb +27 -0
  30. data/examples/aoc2018/day3.rb +71 -0
  31. data/examples/aoc2018/day4.rb +46 -0
  32. data/examples/aoc2018/day5.rb +23 -0
  33. data/examples/aoc2018/day5_part2.rb +29 -0
  34. data/examples/aoc2018/day6.rb +53 -0
  35. data/examples/aoc2018/day7.rb +110 -0
  36. data/examples/aoc2018/day8.rb +39 -0
  37. data/examples/aoc2018/day8_part2.rb +65 -0
  38. data/examples/aoc2018/day9.rb +51 -0
  39. data/examples/aoc2018/day9_circular.rb +125 -0
  40. data/examples/aoc2018/inputs/day10_input.txt +395 -0
  41. data/examples/aoc2018/inputs/day11_input.txt +1 -0
  42. data/examples/aoc2018/inputs/day12_input.txt +34 -0
  43. data/examples/aoc2018/inputs/day12_input2.txt +16 -0
  44. data/examples/aoc2018/inputs/day13_input.txt +150 -0
  45. data/examples/aoc2018/inputs/day13_test.txt +6 -0
  46. data/examples/aoc2018/inputs/day14_input.txt +1 -0
  47. data/examples/aoc2018/inputs/day15_input.txt +32 -0
  48. data/examples/aoc2018/inputs/day15_input10.txt +32 -0
  49. data/examples/aoc2018/inputs/day15_input11.txt +32 -0
  50. data/examples/aoc2018/inputs/day15_input12.txt +32 -0
  51. data/examples/aoc2018/inputs/day15_input13.txt +32 -0
  52. data/examples/aoc2018/inputs/day15_input14.txt +32 -0
  53. data/examples/aoc2018/inputs/day15_input2.txt +7 -0
  54. data/examples/aoc2018/inputs/day15_input3.txt +9 -0
  55. data/examples/aoc2018/inputs/day15_input4.txt +7 -0
  56. data/examples/aoc2018/inputs/day15_input5.txt +7 -0
  57. data/examples/aoc2018/inputs/day15_input6.txt +7 -0
  58. data/examples/aoc2018/inputs/day15_input7.txt +7 -0
  59. data/examples/aoc2018/inputs/day15_input8.txt +5 -0
  60. data/examples/aoc2018/inputs/day15_input9.txt +7 -0
  61. data/examples/aoc2018/inputs/day15_test.txt +9 -0
  62. data/examples/aoc2018/inputs/day16_input.txt +3865 -0
  63. data/examples/aoc2018/inputs/day17_input.txt +2229 -0
  64. data/examples/aoc2018/inputs/day17_input_test.txt +8 -0
  65. data/examples/aoc2018/inputs/day18_input.txt +50 -0
  66. data/examples/aoc2018/inputs/day18_test.txt +10 -0
  67. data/examples/aoc2018/inputs/day19_input.txt +48 -0
  68. data/examples/aoc2018/inputs/day1_input.txt +955 -0
  69. data/examples/aoc2018/inputs/day20_input.txt +1 -0
  70. data/examples/aoc2018/inputs/day21_input.txt +32 -0
  71. data/examples/aoc2018/inputs/day22_input.txt +2 -0
  72. data/examples/aoc2018/inputs/day23_input.txt +1000 -0
  73. data/examples/aoc2018/inputs/day23_input2.txt +9 -0
  74. data/examples/aoc2018/inputs/day24_input.txt +24 -0
  75. data/examples/aoc2018/inputs/day25_input.txt +1483 -0
  76. data/examples/aoc2018/inputs/day2_input.txt +250 -0
  77. data/examples/aoc2018/inputs/day3_input.txt +1233 -0
  78. data/examples/aoc2018/inputs/day4_input.txt +1140 -0
  79. data/examples/aoc2018/inputs/day5_input.txt +1 -0
  80. data/examples/aoc2018/inputs/day6_input.txt +50 -0
  81. data/examples/aoc2018/inputs/day7_input.txt +101 -0
  82. data/examples/aoc2018/inputs/day8_input.txt +1 -0
  83. data/examples/aoc2018/inputs/day9_input.txt +1 -0
  84. data/lib/cem/ccommon.rb +67 -12
  85. data/lib/cem/cflame/popen.rb +89 -0
  86. data/lib/cem/cflame.rb +2 -0
  87. data/lib/cem/cruzzles.rb +198 -50
  88. data/lib/cem/version.rb +1 -1
  89. data/lib/cem.rb +0 -6
  90. 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)