example command to run
$ time ruby xkcd.rb big.txt
100 | |
one,1 | |
two,2 | |
three,3 | |
seven,7 |
# frozen_string_literal: true | |
class Xkcd | |
ITEM = Struct.new(:name, :cost) | |
def initialize(filename) | |
fh = File.open(filename) | |
@answers = [] | |
@total = pennies(fh.readline) | |
@items = fh.each_line.map do |line| | |
item = line.split(',') | |
raise 'invalid file' unless item.count == 2 | |
ITEM.new(item.first, pennies(item.last)) | |
end | |
fh.close | |
end | |
def run | |
item_pack([], items.sort_by(&:cost)) | |
answers.each do |answer| | |
puts answer.map(&:name).join(', ') | |
end | |
end | |
def item_pack(left, right) | |
while (curr = right.pop) | |
sublist = left + [curr] | |
subtotal = sum(sublist) | |
next if subtotal > total | |
@answers << sublist if subtotal == total | |
item_pack(sublist, right + [curr]) | |
end | |
end | |
attr_reader :answers | |
private | |
attr_reader :total, :items | |
def pennies(value) | |
value.gsub(/[^0-9]/, '').to_i | |
end | |
def sum(items) | |
items.reduce(0) { |sum, i| sum + i.cost } | |
end | |
end | |
Xkcd.new(ARGV.first).run |