Skip to content

Commit

Permalink
Year 2016: Day 11
Browse files Browse the repository at this point in the history
  • Loading branch information
joshleaves committed Mar 9, 2024
1 parent 8137aed commit c5945a1
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ I'm also adding notes that may be useful if you're learning Ruby.

Notes for solving:
* [2015, complete](year_2015.md)
* [2016, up to day 08](year_2016.md)
* [2016, up to day 11](year_2016.md)
* [2023, up to day 04](year_2023.md)

# How to use
Expand Down
4 changes: 4 additions & 0 deletions spec/year_2016/day_11_input
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The first floor contains a polonium generator, a thulium generator, a thulium-compatible microchip, a promethium generator, a ruthenium generator, a ruthenium-compatible microchip, a cobalt generator, and a cobalt-compatible microchip.
The second floor contains a polonium-compatible microchip and a promethium-compatible microchip.
The third floor contains nothing relevant.
The fourth floor contains nothing relevant.
4 changes: 4 additions & 0 deletions spec/year_2016/day_11_sample_one
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The first floor contains a hydrogen-compatible microchip and a lithium-compatible microchip.
The second floor contains a hydrogen generator.
The third floor contains a lithium generator.
The fourth floor contains nothing relevant.
34 changes: 34 additions & 0 deletions spec/year_2016/day_11_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require 'year_2016/day_11'

describe Year2016::Day11 do
context 'when Part 1' do
subject(:sample_one) do
described_class.new(File.read('spec/year_2016/day_11_sample_one'), true)
end

it 'checks for safe states' do
%w(01020 11120 22220 12120 02020 12121 22222 32323 22223 33233 23232).each do |input|
input = input.split.map(&:to_i)
expect(described_class).to be_state_is_safe(input)
end
end

it 'gives a final result' do
expect(sample_one.solve).to eq(11)
end
end

context 'when Results' do
subject(:input_data) do
File.read('spec/year_2016/day_11_input')
end

it 'correctly answers part 1' do
expect(described_class.new(input_data, true).solve).to eq(47)
end

it 'correctly answers part 2', skip: 'Test is too slow for CI' do
expect(described_class.new(input_data).solve).to eq(71)
end
end
end
17 changes: 17 additions & 0 deletions year_2016.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,20 @@ Year2016::Day10
```

This one is a bit similar to the [Dining philosophers problem](https://en.wikipedia.org/wiki/Dining_philosophers_problem) in idea, but we are far from the concept, since the original exercise is all about threading and async.


## Day 11: Radioisotope Thermoelectric Generators

```
Year2016::Day11
when Part 1
checks for safe states
gives a final result
when Results
correctly answers part 1
correctly answers part 2 (PENDING: Test is too slow for CI)
```

It seems this exercise got [some people to give up](https://markheath.net/post/aoc-2016-day11), and quite frankly, I felt like it for a while too. In the end, brute-forcing wasn't efficient, but trying a proper implementation of [A* (A-Star)](https://en.wikipedia.org/wiki/A*_search_algorithm) will solve it in a good time.

Now, as for part two... I hope you got twelve minutes to run tests.
116 changes: 116 additions & 0 deletions year_2016/day_11.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
class Year2016
class Day11
class << self
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/BlockLength
def next_moves(state)
moves = []
state.each_with_index do |item, idx|
next if idx.zero?
next if item != state[0]

if state[0] < 3
ns = state.dup
ns[0] += 1
ns[idx] += 1
moves.push(ns) if state_is_safe?(ns)
state.each_with_index do |item_in, idx_in|
next if idx_in <= idx
next if item_in != state[0]

ns = state.dup
ns[0] += 1
ns[idx] += 1
ns[idx_in] += 1
moves.push(ns) if state_is_safe?(ns)
end
end

next unless state[0] >= 1

ns = state.dup
ns[0] -= 1
ns[idx] -= 1
moves.push(ns) if state_is_safe?(ns)
state.each_with_index do |item_in, idx_in|
next if idx_in <= idx
next if item_in != state[0]

ns = state.dup
ns[0] -= 1
ns[idx] -= 1
ns[idx_in] -= 1
moves.push(ns) if state_is_safe?(ns)
end
end
moves
end
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/BlockLength

def state_is_safe?(state)
pairs = state[1..].each_slice(2)
pairs.each_with_index do |pair_out, idx_out|
next if pair_out[0] == pair_out[1]

pairs.each_with_index do |pair_in, idx_in|
next if idx_in == idx_out
return false if pair_out[0] == pair_in[1]
end
end
true
end
end

# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
def initialize(input_file, input_part_one = false)
@version = input_part_one ? 1 : 2
@alltypes = []
@chips = {}
@gens = {}
input_file.chomp.split("\n").each_with_index do |input_line, idx|
next if input_line.match?(/contains nothing relevant.$/)

input_line.scan(/(\w+)-compatible/).flatten.each do |chip|
@chips[chip] = idx
end
input_line.scan(/(\w+) generator/).flatten.each do |gen|
@gens[gen] = idx
end
end
unless input_part_one
@chips['elerium'] = 0
@gens['elerium'] = 0
@chips['dilithium'] = 0
@gens['dilithium'] = 0
end
@alltypes = [[@chips.keys, @gens.keys].flatten.uniq * 2].flatten.sort
@alltypes = ['E', @alltypes].flatten
@initial_state = @alltypes.map.each_with_index do |type, idx|
next 0 if type == 'E'

(idx.odd? ? @chips : @gens)[type]
end
@cnt = 0
end
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity

def search(states, moves_tried, depth)
# puts "DEPTH: #{depth} => #{states.length} (#{moves_tried.length})"
return depth if states.any?{|state| state.all?(3) }

next_states = []
states.sort.reverse_each do |state|
next if moves_tried.include?(state)

moves_tried.add(state)
Day11.next_moves(state).each do |next_state|
next_states.push(next_state) unless moves_tried.include?(next_state)
end
end
search(next_states, moves_tried, depth + 1)
end

def solve
search([@initial_state], Set.new, 0)
end
end
end

0 comments on commit c5945a1

Please sign in to comment.