From: Cristi BALAN Date: 2004-10-04T10:40:53+09:00 Subject: Re: [QUIZ] Secret Santas (#2) Yay, I think I just made my first, hopefully functional, piece of ruby code :) Took me about 2.5 hours, including finding out about how to use Comparable and define <=> :) ... including a long time to realize delete_if is destructive :( The solution does the following: - groups persons by family, - orders the families descending by number of members - gets a santa as a member from the family with the most number of members - for this santa, finds a santee as a member from the family with the mose number of members, that is not the santa's family rinse, repeat until there are no more santas and all is ok or, until we can't find a santee for a santa and decide we can't hook these people up this seems ok, acts ok, but i can't really prove that it actually works across all possible cases :) Here's the code, sorry, it doesn't read from stdin, but it generates it's own random input. beware, this thing is probably badly coded :) ##################### #!ruby # evilchelu@irc # mental@gmail def generate_input(persons=10, families=3) fams = Array.new(families) {|i| 1} srand Time.now.to_i (persons-families).times do fams[rand(families)] += 1 end s = '' fams.each_with_index do |fam, famnr| fam.times do |i| s += (97+i).chr + ' ' + (90-famnr).chr + ' ' + '<' + (97+i).chr + '@' + (90-famnr).chr + ".fam>\n" end end return s.sort_by {rand} end class Object def deep_clone Marshal::load(Marshal.dump(self)) end end class Families < Array def sum_persons s = 0 self.each { |el| s+= el.persons.size} return s end def get_person(exceptfamily='') self.find_all{ |fam| fam.name != exceptfamily }.sort.reverse.first.persons.shift end end class Family include Comparable attr_accessor :name, :persons def initialize(name) @name = name @persons = Array.new end def to_s s = "Family: #{@name}\n" persons.each {|p| s+= " " + p.to_s + "\n"} return s end def <=>(other) persons.size <=> other.persons.size end end class Person include Comparable attr_accessor :name, :family, :email def initialize(args) @name = args[0] @family = args[1] @email = args[2] end def to_s return [@name, @family, @email].join(" ").strip end def <=>(other) tmp = family <=> other.family tmp = name <=> other.name if tmp == 0 return tmp end end def read_data(input) puts "Input\n#{input}\n" fams = Families.new persons = Families.new input.each do |line| persons.push Person.new(line.split(/\s/, 3)) fam = fams.find { |fam| fam.name == persons.last.family} if fam.nil? fam = Family.new(persons.last.family) fams.push fam end fam.persons.push persons.last unless fam.persons.find{ |pers| pers.name == persons.last.name} end return fams end def make_santas(fams) fams = fams.sort.reverse santas = fams.deep_clone santees = fams.deep_clone s = "List of santas (SANTA - SANTEE)\n" while santas.sum_persons > 0 begin santa = santas.get_person santee = santees.get_person(santa.family) rescue end if (!santa || !santee) puts "Bad input. Red Sleigh Down" return end s += santa.to_s + ' - ' + santee.to_s + "\n" end puts s end make_santas(read_data(generate_input())) ##################### -- Cristi BALAN