From b2276efd026055232fe017dad982a3dcbe956d60 Mon Sep 17 00:00:00 2001 From: red Date: Sun, 10 Mar 2024 02:23:23 +0100 Subject: [PATCH] Year 2016: Day 14 --- README.md | 2 +- spec/year_2016/day_14_spec.rb | 29 ++++++++++++++++++ year_2016.md | 15 ++++++++++ year_2016/day_14.rb | 56 +++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 spec/year_2016/day_14_spec.rb create mode 100644 year_2016/day_14.rb diff --git a/README.md b/README.md index f69af86..28cffb1 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 13](year_2016.md) +* [2016, up to day 14](year_2016.md) * [2023, up to day 04](year_2023.md) # How to use diff --git a/spec/year_2016/day_14_spec.rb b/spec/year_2016/day_14_spec.rb new file mode 100644 index 0000000..0b80e66 --- /dev/null +++ b/spec/year_2016/day_14_spec.rb @@ -0,0 +1,29 @@ +require 'year_2016/day_14' + +describe Year2016::Day14 do + context 'when Part 1' do + it 'gives a final result' do + expect(described_class.new('abc', true).to_i).to eq(22_728) + end + end + + context 'when Part 2' do + it 'gives a final result', skip: 'Test is too slow for CI' do + expect(described_class.new('abc').to_i).to eq(22_551) + end + end + + context 'when Results' do + subject(:input_data) do + 'yjdafjpo' + end + + it 'correctly answers part 1' do + expect(described_class.new(input_data, true).to_i).to eq(25_427) + end + + it 'correctly answers part 2', skip: 'Test is too slow for CI' do + expect(described_class.new(input_data).to_i).to eq(22_045) + end + end +end diff --git a/year_2016.md b/year_2016.md index 5928184..4043921 100644 --- a/year_2016.md +++ b/year_2016.md @@ -187,3 +187,18 @@ Year2016::Day13 ``` Thinking in terms of X and Y can be a source of pain, but this one was straight enough. + +## Day 14: One-Time Pad + +``` +Year2016::Day14 + when Part 1 + gives a final result + when Part 2 + gives a final result + when Results + correctly answers part 1 + correctly answers part 2 +``` + +This one is even more annoying than [the last one](year_2016.md#day-05-how-about-a-nice-game-of-chess). diff --git a/year_2016/day_14.rb b/year_2016/day_14.rb new file mode 100644 index 0000000..00eb246 --- /dev/null +++ b/year_2016/day_14.rb @@ -0,0 +1,56 @@ +require 'digest' + +class Year2016 + class Day14 + def initialize(input_data, input_part_one = false) + @version = input_part_one ? 1 : 2 + @input = input_data.chomp + end + + def md5 + @md5 ||= Digest::MD5.new + end + + def hash(str) + md5.reset + md5 << str + return md5.hexdigest if @version == 1 + + digest = md5.hexdigest + 1.upto(2016) do + md5.reset + md5 << digest + digest = md5.hexdigest + end + digest + end + + def generate_hashes(starter, length) + 0.upto(length).map{|cnt| hash("#{@input}#{starter + cnt}") } + end + + # rubocop:disable Metrics/AbcSize, Metrics/MethodLength + def to_i + cnt = -1 + hash_queue = generate_hashes(0, 1000) + keys = [] + loop do + cnt += 1 + hash_queue.concat(generate_hashes(cnt, 1000)) if hash_queue.empty? + digest = hash_queue.shift + next unless digest =~ /(.)\1\1/ + + letter = $1 + hash_queue.concat(generate_hashes(cnt + 1 + hash_queue.length, 1000 - hash_queue.length)) + 0.upto(999) do |idx| + next unless hash_queue[idx].include?(letter * 5) + return cnt if keys.length == 63 + + keys.push(digest) + break + end + end + end + # rubocop:enable Metrics/AbcSize, Metrics/MethodLength + end +end