cem 0.1.3 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)