Skip to content

Commit

Permalink
Year 2015: Day 04
Browse files Browse the repository at this point in the history
  • Loading branch information
joshleaves committed Feb 6, 2024
1 parent 126c3bd commit 857e49a
Show file tree
Hide file tree
Showing 7 changed files with 342 additions and 3 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ 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 01](year_2015.md)
* [2015, up to day 04](year_2015.md)
* [2023, up to day 03](year_2023.md)

# How to use
Install [RSpec](https://github.com/rspec/rspec-metagem) with `gem install rspec` and run `rspec` in the root directory.

# Testing guidelines
The tests are written with [Test-driven Development](https://en.wikipedia.org/wiki/Test-driven_development) principles by using the input and results provided by each exercise's examples.
The tests are written with [Test-driven Development](https://en.wikipedia.org/wiki/Test-driven_development) principles by using the input and results provided by each exercise's samples. The full input is then used in the tests, and the expected results are validated on the website.

A [GitHub Action](https://docs.github.com/en/actions) is available to test the code as soon as it is pushed.
194 changes: 194 additions & 0 deletions spec/year_2015/day_04_input

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions spec/year_2015/day_04_sample_one
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
6 changes: 6 additions & 0 deletions spec/year_2015/day_04_sample_two
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
62 changes: 62 additions & 0 deletions spec/year_2015/day_04_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
require 'year_2015/day_04'

describe Year2015::Day04 do
context 'Part 1' do
subject do
Year2015::Day04.new(File.read('spec/year_2015/day_04_sample_one'), true)
end

it 'finds winning numbers' do
[
[17, 48, 83, 86],
[32, 61],
[1, 21],
[84],
[],
[]
].each_with_index do |result, i|
expect(subject.lines[i].winning_numbers).to eq(result)
end
end

it 'calculates points' do
[8, 2, 2, 1, 0, 0].each_with_index do |result, i|
expect(subject.lines[i].to_i).to eq(result)
end
end

it 'gives a final result' do
expect(subject.to_i).to eq(13)
end
end

context 'Part 2' do
subject do
Year2015::Day04.new(File.read('spec/year_2015/day_04_sample_one'))
end

it 'distributes cards' do
[1, 2, 4, 8, 14, 1].each_with_index do |result, i|
expect(subject.lines[i].quantity).to eq(result)
end
end

it 'gives a final result' do
expect(subject.to_i).to eq(30)
end
end

context 'Results' do
subject do
File.read('spec/year_2015/day_04_input')
end

it 'correctly answers part 1' do
expect(Year2015::Day04.new(subject, true).to_i).to eq(17803)
end

it 'correctly answers part 2' do
expect(Year2015::Day04.new(subject).to_i).to eq(5554894)
end
end
end
22 changes: 21 additions & 1 deletion year_2015.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,24 @@ Year2015::Day03

The only thing to be wary of is on line 16: without the call to `#dup`, all of Santa's and Robo-Santa's positions will be overwritten, since Ruby's object model has a tendancy to pass references when you expect to pass values.

Passing by value or reference is a really wonky subject, but this [blog post](https://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/) got nice examples that will get you started.
Passing by value or reference is a really wonky subject, but this [blog post](https://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/) got nice examples that will get you started.

## Day 04

```
Year2015::Day04
Part 1
finds winning numbers
calculates points
gives a final result
Part 2
distributes cards
gives a final result
Results
correctly answers part 1
correctly answers part 2
```

Part one is pretty easy. One useful method is the [`#&`](https://ruby-doc.org/core-3.0.1/Array.html#method-i-26) operator that takes two arrays and returns a new one with only the matching contents. The syntax is clearly inspired by [bit masking](https://en.wikipedia.org/wiki/Mask_(computing)), which is a subject you should look into if you've never heard of it.

Part two is a bit more annoying and requires to do a two-pass, but nothing really improbable.
51 changes: 51 additions & 0 deletions year_2015/day_04.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
class Year2015
class Day04
attr_reader :version, :lines

class Line
attr_accessor :quantity

def initialize(input_line)
@line = input_line
@winners = @line.split(':').last.split('|').first.scan(/\d+/).map(&:to_i)
@inputs = @line.split('|').last.scan(/\d+/).map(&:to_i)
@quantity = 1
end

def winning_numbers
(@winners & @inputs).sort
end

def to_i
return 0 if winning_numbers.empty?

2**(winning_numbers.length - 1)
end
end

def initialize(input_file, input_part_one = false)
@version = input_part_one ? 1 : 2
@lines = input_file.chomp.split("\n").map do |input_line|
Line.new(input_line)
end
distribute_cards if version == 2
end

def distribute_cards
@lines.each_with_index do |line, i|
cards_won = line.winning_numbers.length
1.upto(cards_won) do |j|
next unless @lines[i + j]

@lines[i + j].quantity += line.quantity
end
end
end

def to_i
return @lines.map(&:quantity).inject(&:+) if version == 2

@lines.map(&:to_i).inject(&:+)
end
end
end

0 comments on commit 857e49a

Please sign in to comment.