From b663d89ea91291bdaf0b78c37431a943e32f6435 Mon Sep 17 00:00:00 2001 From: red Date: Wed, 13 Mar 2024 06:35:59 +0100 Subject: [PATCH] Year 2016: Day 21 --- README.md | 2 +- spec/year_2016/day_21_input | 100 ++++++++++++++++++++++++++++++++++ spec/year_2016/day_21_spec.rb | 36 ++++++++++++ year_2016.md | 13 +++++ year_2016/day_11.rb | 1 - year_2016/day_21.rb | 89 ++++++++++++++++++++++++++++++ 6 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 spec/year_2016/day_21_input create mode 100644 spec/year_2016/day_21_spec.rb create mode 100644 year_2016/day_21.rb diff --git a/README.md b/README.md index e1ba72e..c427e43 100644 --- a/README.md +++ b/README.md @@ -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 20](year_2016.md) +* [2016, up to day 21](year_2016.md) * [2023, up to day 04](year_2023.md) # How to use diff --git a/spec/year_2016/day_21_input b/spec/year_2016/day_21_input new file mode 100644 index 0000000..d7101be --- /dev/null +++ b/spec/year_2016/day_21_input @@ -0,0 +1,100 @@ +reverse positions 1 through 6 +rotate based on position of letter a +swap position 4 with position 1 +reverse positions 1 through 5 +move position 5 to position 7 +swap position 4 with position 0 +swap position 4 with position 6 +rotate based on position of letter a +swap position 0 with position 2 +move position 5 to position 2 +move position 7 to position 1 +swap letter d with letter c +swap position 5 with position 3 +reverse positions 3 through 7 +rotate based on position of letter d +swap position 7 with position 5 +rotate based on position of letter f +swap position 4 with position 1 +swap position 3 with position 6 +reverse positions 4 through 7 +rotate based on position of letter c +move position 0 to position 5 +swap position 7 with position 4 +rotate based on position of letter f +reverse positions 1 through 3 +move position 5 to position 3 +rotate based on position of letter g +reverse positions 2 through 5 +rotate right 0 steps +rotate left 0 steps +swap letter f with letter b +rotate based on position of letter h +move position 1 to position 3 +reverse positions 3 through 6 +rotate based on position of letter h +swap position 4 with position 3 +swap letter b with letter h +swap letter a with letter h +reverse positions 1 through 6 +swap position 3 with position 6 +swap letter e with letter d +swap letter e with letter h +swap position 1 with position 5 +rotate based on position of letter a +reverse positions 4 through 5 +swap position 0 with position 4 +reverse positions 0 through 3 +move position 7 to position 2 +swap letter e with letter c +swap position 3 with position 4 +rotate left 3 steps +rotate left 7 steps +rotate based on position of letter e +reverse positions 5 through 6 +move position 1 to position 5 +move position 1 to position 2 +rotate left 1 step +move position 7 to position 6 +rotate left 0 steps +reverse positions 5 through 6 +reverse positions 3 through 7 +swap letter d with letter e +rotate right 3 steps +swap position 2 with position 1 +swap position 5 with position 7 +swap letter h with letter d +swap letter c with letter d +rotate based on position of letter d +swap letter d with letter g +reverse positions 0 through 1 +rotate right 0 steps +swap position 2 with position 3 +rotate left 4 steps +rotate left 5 steps +move position 7 to position 0 +rotate right 1 step +swap letter g with letter f +rotate based on position of letter a +rotate based on position of letter b +swap letter g with letter e +rotate right 4 steps +rotate based on position of letter h +reverse positions 3 through 5 +swap letter h with letter e +swap letter g with letter a +rotate based on position of letter c +reverse positions 0 through 4 +rotate based on position of letter e +reverse positions 4 through 7 +rotate left 4 steps +swap position 0 with position 6 +reverse positions 1 through 6 +rotate left 2 steps +swap position 5 with position 3 +swap letter b with letter d +swap letter b with letter d +rotate based on position of letter d +rotate based on position of letter c +rotate based on position of letter h +move position 4 to position 7 \ No newline at end of file diff --git a/spec/year_2016/day_21_spec.rb b/spec/year_2016/day_21_spec.rb new file mode 100644 index 0000000..a957569 --- /dev/null +++ b/spec/year_2016/day_21_spec.rb @@ -0,0 +1,36 @@ +require 'year_2016/day_21' + +describe Year2016::Day21 do + context 'when Part 1' do + subject(:sample_one) do + <<~HEREDOC + swap position 4 with position 0 + swap letter d with letter b + reverse positions 0 through 4 + rotate left 1 step + move position 1 to position 4 + move position 3 to position 0 + rotate based on position of letter b + rotate based on position of letter d + HEREDOC + end + + it 'gives a final result' do + expect(described_class.new(sample_one, true).solve('abcde')).to eq('decab') + end + end + + context 'when Results' do + subject(:input_data) do + File.read('spec/year_2016/day_21_input') + end + + it 'correctly answers part 1' do + expect(described_class.new(input_data, true).solve('abcdefgh')).to eq('gfdhebac') + end + + it 'correctly answers part 2' do + expect(described_class.new(input_data).solve('fbgdceah')).to eq('dhaegfbc') + end + end +end diff --git a/year_2016.md b/year_2016.md index 13a68e0..ddf8a9a 100644 --- a/year_2016.md +++ b/year_2016.md @@ -292,3 +292,16 @@ Year2016::Day20 ``` Dealing with indexes inclusion can often be a pain in counting. + +## Day 21: Scrambled Letters and Hash + +``` +Year2016::Day21 + when Part 1 + gives a final result + when Results + correctly answers part 1 + correctly answers part 2 +``` + +While [some of my counterparts](https://github.com/rHermes/adventofcode/blob/master/2016/21/y2016_d21_p02.py) tried using complete brute-force approach to get the original password, I found reversing the instructions to be much more funnier. diff --git a/year_2016/day_11.rb b/year_2016/day_11.rb index 86ca53d..1831dd2 100644 --- a/year_2016/day_11.rb +++ b/year_2016/day_11.rb @@ -94,7 +94,6 @@ def initialize(input_file, input_part_one = false) # 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 = [] diff --git a/year_2016/day_21.rb b/year_2016/day_21.rb new file mode 100644 index 0000000..d2f7aaf --- /dev/null +++ b/year_2016/day_21.rb @@ -0,0 +1,89 @@ +class Year2016 + class Day21 + def initialize(input_data, input_part_one = false) + @version = input_part_one ? 1 : 2 + @instructions = input_data.lines.map do |instruction| + compile_instruction(instruction.chomp) + end + end + + # rubocop:disable Metrics/MethodLength + def compile_instruction(instruction) + case instruction + when /^swap position (\d+) with position (\d+)$/ + [:swap_positions, $1.to_i, $2.to_i] + when /^swap letter (\w) with letter (\w)$/ + [:swap_letters, $1, $2] + when /^rotate (\bleft\b|\bright\b) (\d+) steps?$/ + [:"rotate_#{$1}", $2.to_i] + when /^rotate based on position of letter (\w)$/ + [:rotate_based_on, $1] + when /^reverse positions (\d+) through (\d+)$/ + [:reverse, $1.to_i, $2.to_i] + when /^move position (\d+) to position (\d+)$/ + [:move, $1.to_i, $2.to_i] + else + raise "Unknown instruction: #{instruction}" + end + end + # rubocop:enable Metrics/MethodLength + + def swap_positions(from, to) + @password[from], @password[to] = @password[to], @password[from] + end + + def swap_letters(from, to) + @password.gsub!(from, '_') + @password.gsub!(to, from) + @password.gsub!('_', to) + end + + def reverse(from, to) + @password[from..to] = @password[from..to].reverse + end + + def _rotate(input, steps) + input.chars.rotate(steps).join + end + + def rotate_left(steps) + @password = _rotate(@password, @version == 1 ? steps : steps * -1) + end + + def rotate_right(steps) + @password = _rotate(@password, @version == 2 ? steps : steps * -1) + end + + def move(from, to) + from, to = to, from if @version == 2 + pass_arr = @password.chars + extract = pass_arr.delete_at(from) + pass_arr.insert(to, extract) + @password = pass_arr.join + end + + def _rotate_based_on(input, index) + index = input.index(index) + index += index >= 4 ? 2 : 1 + _rotate(input, index * -1) + end + + def rotate_based_on(index) + return @password = _rotate_based_on(@password, index) if @version == 1 + + 1.upto(@password.length) do |i| + str = _rotate(@password, i * - 1) + return @password = str if _rotate_based_on(str, index) == @password + end + raise 'NOT FOUND' + end + + def solve(input_password) + @password = input_password + @instructions.send(@version == 2 ? :reverse_each : :each) do |instruction| + send(*instruction) + end + @password + end + end +end