Pastie now auto-senses if line-wrap is a bad or good idea. Feedback?
## mark a section (Learn more)
class Array def rand uniqs = self.select{|e| e.uniq.size == e.size} uniqs.empty? ? self[Kernel.rand(length)] : uniqs[Kernel.rand(uniqs.length)] end def unordered_include?(other) self.map{|e| e.map{|s| s.to_s}.sort}.include? other.map{|s| s.to_s}.sort end end class Hash def comb(group_size) result = [] inner_comb = lambda do |head,tail| tail[0..-(group_size-head.size)].each do |e| if (head.size >= group_size-1) tail.each {|t| result << head + [t]} else inner_comb[head + [e], tail[tail.index(e)+1..-1]] end end end inner_comb[[],self.inject([]) {|a,v| v[1].times{a <<v[0]}; a}] result.uniq end def remove_set(set) set.each {|e| self[e] -= 1} end end def mix_and_match(candles, recipients, candles_per_recipient) return ERROR_STRING if ((candles.values.inject{|a,v| a+v}) < (recipients.size * candles_per_recipient)) candle_set = recipients.inject({}) do |a,v| tried = [] tries = 0 loop do random_pick = candles.comb(candles_per_recipient).rand tried << random_pick unless tried.unordered_include? random_pick break unless a.values.unordered_include? random_pick break if (tries+=1) > candles.values.size * 2 #not perfect, but good enough end candles.remove_set(tried.last) a[v] = tried.last a end candle_set.merge({:extra => candles}) end
This paste will be private.
From the Design Piracy series on my blog: