diff --git a/.rspec b/.rspec index 1063158..2c8f8e5 100644 --- a/.rspec +++ b/.rspec @@ -1,2 +1,3 @@ --require spec_helper +--fail-fast -I . diff --git a/README.md b/README.md index aad9aee..47c4f33 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Consistency is hard without proper goals to set a good (let's dare say "atomic") I'm also adding notes that may be useful if you're learning Ruby. Notes for solving: -* [2015, up to day 18](year_2015.md) +* [2015, up to day 19](year_2015.md) * [2023, up to day 03](year_2023.md) # How to use diff --git a/spec/year_2015/day_19_input b/spec/year_2015/day_19_input new file mode 100644 index 0000000..2714445 --- /dev/null +++ b/spec/year_2015/day_19_input @@ -0,0 +1,45 @@ +Al => ThF +Al => ThRnFAr +B => BCa +B => TiB +B => TiRnFAr +Ca => CaCa +Ca => PB +Ca => PRnFAr +Ca => SiRnFYFAr +Ca => SiRnMgAr +Ca => SiTh +F => CaF +F => PMg +F => SiAl +H => CRnAlAr +H => CRnFYFYFAr +H => CRnFYMgAr +H => CRnMgYFAr +H => HCa +H => NRnFYFAr +H => NRnMgAr +H => NTh +H => OB +H => ORnFAr +Mg => BF +Mg => TiMg +N => CRnFAr +N => HSi +O => CRnFYFAr +O => CRnMgAr +O => HP +O => NRnFAr +O => OTi +P => CaP +P => PTi +P => SiRnFAr +Si => CaSi +Th => ThCa +Ti => BP +Ti => TiTi +e => HF +e => NAl +e => OMg + +ORnPBPMgArCaCaCaSiThCaCaSiThCaCaPBSiRnFArRnFArCaCaSiThCaCaSiThCaCaCaCaCaCaSiRnFYFArSiRnMgArCaSiRnPTiTiBFYPBFArSiRnCaSiRnTiRnFArSiAlArPTiBPTiRnCaSiAlArCaPTiTiBPMgYFArPTiRnFArSiRnCaCaFArRnCaFArCaSiRnSiRnMgArFYCaSiRnMgArCaCaSiThPRnFArPBCaSiRnMgArCaCaSiThCaSiRnTiMgArFArSiThSiThCaCaSiRnMgArCaCaSiRnFArTiBPTiRnCaSiAlArCaPTiRnFArPBPBCaCaSiThCaPBSiThPRnFArSiThCaSiThCaSiThCaPTiBSiRnFYFArCaCaPRnFArPBCaCaPBSiRnTiRnFArCaPRnFArSiRnCaCaCaSiThCaRnCaFArYCaSiRnFArBCaCaCaSiThFArPBFArCaSiRnFArRnCaCaCaFArSiRnFArTiRnPMgArF \ No newline at end of file diff --git a/spec/year_2015/day_19_sample_one b/spec/year_2015/day_19_sample_one new file mode 100644 index 0000000..1798c27 --- /dev/null +++ b/spec/year_2015/day_19_sample_one @@ -0,0 +1,5 @@ +H => HO +H => OH +O => HH + +HOH diff --git a/spec/year_2015/day_19_sample_two b/spec/year_2015/day_19_sample_two new file mode 100644 index 0000000..70905c4 --- /dev/null +++ b/spec/year_2015/day_19_sample_two @@ -0,0 +1,7 @@ +e => H +e => O +H => HO +H => OH +O => HH + +HOHOHO diff --git a/spec/year_2015/day_19_spec.rb b/spec/year_2015/day_19_spec.rb new file mode 100644 index 0000000..255c689 --- /dev/null +++ b/spec/year_2015/day_19_spec.rb @@ -0,0 +1,41 @@ +require 'year_2015/day_19' + +describe Year2015::Day19 do + context 'when Part 1' do + subject(:sample_one) do + described_class.new(File.read('spec/year_2015/day_19_sample_one'), true) + end + + it 'calculates all permutations' do + expect(sample_one.permutations).to eq(%w(HOOH HOHO OHOH HHHH).sort) + end + + it 'gives a final result' do + expect(sample_one.to_i).to eq(4) + end + end + + context 'when Part 2' do + subject(:sample_two) do + described_class.new(File.read('spec/year_2015/day_19_sample_two')) + end + + it 'gives a final result' do + expect(sample_two.build_molecule).to eq(5) + end + end + + context 'when Results' do + subject(:input_data) do + File.read('spec/year_2015/day_19_input') + end + + it 'correctly answers part 1' do + expect(described_class.new(input_data, true).to_i).to eq(576) + end + + it 'correctly answers part 2' do + expect(described_class.new(input_data).build_molecule).to eq(207) + end + end +end diff --git a/year_2015.md b/year_2015.md index 66cec5c..6e0d760 100644 --- a/year_2015.md +++ b/year_2015.md @@ -312,3 +312,19 @@ Year2015::Day18 I already touched upon [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) earlier, and this exercise is a good example of it. I thought I was being smart by only keeping track of positions of only ON lights, which was a good implementation for a six-by-six data set, but proved UTTERLY SLOW on the final data set. Using an array of strings (as is the most naive implementation) is indeed the fastest way to do it. I still kept the old code commented. + +## Day 19 + +``` +Year2015::Day19 + when Part 1 + calculates all permutations + gives a final result + when Part 2 + gives a final result + when Results + correctly answers part 1 + correctly answers part 2 +``` + +Part one is nothing to write home about. However, part two... First idea was the good ol' bruteforce approach, but when searching if there was an algorithm I didn't know, I stumbled upon a [very interesting Reddit comment](https://www.reddit.com/r/adventofcode/comments/3xflz8/day_19_solutions/cy4h7ji/)... diff --git a/year_2015/day_19.rb b/year_2015/day_19.rb new file mode 100644 index 0000000..ed2cd1f --- /dev/null +++ b/year_2015/day_19.rb @@ -0,0 +1,55 @@ +class Year2015 + class Day19 + attr_accessor :starter, :molecules + + RE_MOLECULE = /^(\w+) => (\w+)$/ + + def initialize(input_data, input_part_one = false) + @version = input_part_one ? 1 : 2 + input_data = input_data.chomp.split("\n").reject(&:empty?) + @starter = input_data.pop + @molecules = {} + input_data.map do |input_line| + input_line =~ RE_MOLECULE + @molecules[$1] ||= [] + @molecules[$1].push($2) + [$1, $2] + end + end + + def do_permutations(input, replacements) + re_input = /(#{input})/ + parts = starter.scan(re_input).length + replacements.each do |replacement| + 0.upto(parts - 1) do |idx| + new_str = starter.gsub(re_input).each_with_index do |_part, i| + idx == i ? replacement : input + end + @permutations.push(new_str) + end + end + end + + def permutations + @permutations ||= begin + @permutations = [] + @molecules.each do |input, replacements| + do_permutations(input, replacements) + end + @permutations.sort.uniq + end + end + + def to_i + @to_i ||= permutations.uniq.length + end + + def count_str(symbol) + @starter.scan(/#{symbol}/).length + end + + def build_molecule + @starter.scan(/[A-Z]/).length - count_str('Rn') - count_str('Ar') - (2 * count_str('Y')) - 1 + end + end +end