[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

[QUIZ] Secret Santas (#2

James Gray

10/1/2004 12:50:00 PM

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.grayproductions.net/...

3. Enjoy!

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Honoring a long standing tradition started by my wife's dad, my friends all play
a Secret Santa game around Christmas time. We draw names and spend a week
sneaking that person gifts and clues to our identity. On the last night of the
game, we get together, have dinner, share stories, and, most importantly, try to
guess who our Secret Santa was. It's a crazily fun way to enjoy each other's
company during the holidays.

To choose Santas, we use to draw names out of a hat. This system was tedious,
prone to many "Wait, I got myself..." problems. This year, we made a change to
the rules that further complicated picking and we knew the hat draw would not
stand up to the challenge. Naturally, to solve this problem, I scripted the
process. Since that turned out to be more interesting than I had expected, I
decided to share.

This weeks Ruby Quiz is to implement a Secret Santa selection script.

Your script will be fed a list of names on STDIN. An example might be:

Luke Skywalker <luke@theforce.net>
Leia Skywalker <leia@therebellion.org>
Toula Portokalos <toula@manhunter.org>
Gus Portokalos <gus@weareallfruit.net>
Bruce Wayne <bruce@imbatman.com>
Virgil Brigman <virgil@rigworkersunion.org>
Lindsey Brigman <lindsey@iseealiens.net>

Note: If you cannot tell, I made those addresses up and you'll need to replace
them with something meaningful. Please don't pester those people, should they
happen to be real.

The format for these names is:

FIRST_NAME space FAMILY_NAME space <EMAIL_ADDRESS> newline

We'll keep things simple and say that people only have two names, so you don't
have to worry about tricky names like Gray II.

Your script should then choose a Secret Santa for every name in the list.
Obviously, a person cannot be their own Secret Santa. In addition, my friends
no longer allow people in the same family to be Santas for each other and your
script should take this into account.

Output is obvious. E-mail the Santa and tell them who their person is.

The extra credit for this quiz is to convince all your friends how much fun this
can really be, so you can put your script to good use. Go forth spreading
holiday cheer into the world!


55 Answers

Moses Hohman

10/1/2004 9:37:00 PM

0

Hi James,

What should the script do if there is no solution for the input set,
e.g. the set is two people in the same family?

Moses



James Gray

10/1/2004 9:53:00 PM

0

On Oct 1, 2004, at 4:37 PM, Moses Hohman wrote:

> Hi James,
>
> What should the script do if there is no solution for the input set,
> e.g. the set is two people in the same family?

Egad, knew I forgot to mention something! My bad, sorry. Told ya'll I
still need some breaking in at quiz writing. <laughs>

I will only feed your script valid combinations, so if you don't want
to worry about it, don't. If you do want to build in a sanity check, a
simple error message should be fine. Handle it however you are
comfortable.

James Edward Gray II



Moses Hohman

10/2/2004 3:59:00 AM

0

No worries, I probably should have figured that out for myself anyway,
but I felt like asking.

On Oct 1, 2004, at 4:53 PM, James Edward Gray II wrote:

> On Oct 1, 2004, at 4:37 PM, Moses Hohman wrote:
>
>> Hi James,
>>
>> What should the script do if there is no solution for the input set,
>> e.g. the set is two people in the same family?
>
> Egad, knew I forgot to mention something! My bad, sorry. Told ya'll
> I still need some breaking in at quiz writing. <laughs>
>
> I will only feed your script valid combinations, so if you don't want
> to worry about it, don't. If you do want to build in a sanity check,
> a simple error message should be fine. Handle it however you are
> comfortable.
>
> James Edward Gray II
>
>



Gavin Kistner

10/2/2004 4:26:00 AM

0

On Oct 1, 2004, at 6:50 AM, Ruby Quiz wrote:
> Your script will be fed a list of names on STDIN.

I've never worked with STDIN before; does this mean a single call to
#gets will return a newline-delimited list of names, or I should loop
calls to #gets until it returns nil?
--
(-, /\ \/ / /\/



Florian Gross

10/2/2004 9:10:00 AM

0

Gavin Kistner wrote:

>> Your script will be fed a list of names on STDIN.
> I've never worked with STDIN before; does this mean a single call to
> #gets will return a newline-delimited list of names, or I should loop
> calls to #gets until it returns nil?

STDIN and ARGF are both IOs. STDIN reads from the standard input. ARGF
reads from the standard input and file names that are in ARGV.

IO#gets returns one line with a \n at the end or nil when there are no
more lines available.

There's also IO#read which returns the whole file as a String and
IO#readlines which returns the whole file as an Array of lines. (Each
with a \n at the end.)

Regards,
Florian Gross

Joe Cheng

10/2/2004 10:09:00 PM

0

> Output is obvious. E-mail the Santa and tell them who their person is.

Will you accept solutions that just print out the e-mails instead of
sending them? :)

James Gray

10/2/2004 10:23:00 PM

0

On Oct 2, 2004, at 5:09 PM, Joe Cheng wrote:

>> Output is obvious. E-mail the Santa and tell them who their person
>> is.
>
> Will you accept solutions that just print out the e-mails instead of
> sending them? :)

Why? Are you not yet familiar with "net/smtp"? It's a standard lib,
so this is a good excuse to learn it and it's surprisingly simple...
:D

There's no right and wrong answers to a Ruby Quiz. The goal is to
think, learn and have a little fun. Submit whatever does that for you.

James Edward Gray II



Robo

10/3/2004 12:20:00 PM

0

I'm still new to Ruby, but giving this a crack anyway.

I could have made more classes to model the problem better, but didn't
bother. Everything's done by a Hat with higher intelligence than regular
hats (that doesn't deserve a capital H).

To run, type "ruby santa.rb", then enter each person's name and email in
the format specified. When you're done the script tells you the result
of the selection.

The email part is a bit rogue, but that's just a matter of creating a
more comprehensive email message. It's disabled by default, just
uncomment the lines in Hat#notify.

It doesn't take into account of creating loops smaller than total number
of people. i.e. If there're 4 people, 3 of them maybe Secret Santas of
each other, leaving one stranded.

Robo
require 'net/smtp'

#a very smart Hat that even knows how to email
class Hat

#represents a member of the Christmas gathering
Member = Struct.new(:firstName, :lastName, :email, :minion)

def initialize
@members = []
@pool = []
end

#put a new member into the hat
def put(firstName, lastName, email)
member = Member.new(firstName, lastName, email)
@members << member
@pool << member
end

#match each Secret Santa to a person
def match
@members.each do |santa|
santa.minion = draw(santa)
notify(santa)
end
end

#draw a person out of the hat for a Secret Santa
def draw(santa)
validPool = filter(santa)
person = validPool.at(rand(validPool.size))
@pool.delete(person)
end

#filter out people who're in the same family as Secret Santa
def filter(santa)
@pool.select do |member|
member.lastName != santa.lastName
end
end

#notify each Secret Santa who they'll be watching over
def notify(santa)
if santa.minion != nil
msg = "#{santa.firstName} #{santa.lastName} is watching over " +
"#{santa.minion.firstName} #{santa.minion.lastName}"
else
msg = "#{santa.firstName} #{santa.lastName} is watching over nobody"
end

puts msg

#Net::SMTP.start('smtp.example.com', 25) do |smtp|
# smtp.send_message(msg, 'hat@magic.com', santa.email)
#end
end
end

def main
h = Hat.new
while (s = gets()) != nil
s.scan(/^(.*?) (.*?) <(.*?)>$/) do |firstName, lastName, email|
h.put(firstName, lastName, email)
end
end
h.match
end

if __FILE__ == $0
main
end

Thomas Leitner

10/3/2004 1:02:00 PM

0

And here is my version of the second quiz. It does not use 'net/smtp' but shows the chosen santas on the console.

Thomas


-----------------------------------------------
#!/usr/bin/env ruby

Person = Struct.new( :first, :last, :email )

# Parses one line and extract the data items
def parse_name( name )
person = Person.new( *name.split[0..2] )
if person.first.nil? || person.last.nil? || person.email.nil?
puts "Invalid input: #{name.inspect}"
exit
end
return person
end

# Reads lines from the given IO object and returns a Hash with all persons as keys
def parse_names( io )
list = {}
list[parse_name( STDIN.readline )] = nil until io.eof
return list
end

# Associates each person with a list of possibile Santas
def build_santa_lists( list )
list.each_key do |person|
possible_santas = list.keys
possible_santas.reject! { |other_person| other_person.last == person.last }
list[person] = possible_santas
end
end

# A Santa is correct if there is no other person for whom only the selected Santa is left
def verify_santa( list, person, santa )
list.each do |key, value|
return false if key != person && value == [santa]
end
return true
end

# Choose a Santa for each person
def choose_santas( list )
list.each do |person, possible_santas|
begin santa = possible_santas[rand( possible_santas.length )] end until verify_santa( list, person, santa )
list.each_value { |value| value.delete( santa ) if Array === value }
list[person] = santa
end
end

# Mail the Santas which person they have got
def mail_santas( list )
list.each do |person, santa|
santa = Person.new('<no valid santa found>', '', '') if santa.nil?
puts "Santa #{santa.first} #{santa.last} #{santa.email} for #{person.first} #{person.last} #{person.email}"
end
end

list = parse_names( STDIN )
build_santa_lists list
choose_santas list
mail_santas list

Gavin Kistner

10/3/2004 2:35:00 PM

0

My solution to the Quiz #2 is at http://phrogz.net/RubyLi...

The general approach is:
a) Create a randomized array of Person objects (having parsed the
input).
b) Turn the array into a circular list, and separate any adjacent
family members.
c) Send email (or, if $DEBUG is set, spit out a message), treating
adjacent pairs in the list as giver/receivers.

I initially wrote it treating the array as a circular list, but then
decided I wanted a real circular list class. It's not well tested yet,
but if you like, the Ouroboros class I created is free for the horking.
(It's in the above url, as well as documented and available in main
http:/phrogz.net/RubyLibs/)

I thought I had a better, less-hackish solution than the
pseduo-bubble-sort method I used to separate adjacent duplicates. I
sorted people into family bins, randomized the families, and then
pulled people out from one bin at a time. The benefit was supposed to
be better family separation (to limit occurrances of John Doe from
giving to Bob Smith who gave right back to Jane Doe). Unfortunately, I
didn't think of the case of a single many-member family overpowering
the pool, where early even distribution ends up with only members from
that family left at the end.

I was thinking about weighting my choices by the number of people in
each bin, but since the above solution works, I decided to scrap it.


The interesting thing about this quiz is...my extended family does the
same thing, and up until now no one had thought to automate the
no-same-family rule. The list of members was randomized in excel (much
like Ruby: sort names by an adjacent random number column) and then the
list manually massaged to ensure no family members were adjacent.

I'd never worked with STDIN or SMTP before, so this was a great
exercise for me. Looking forward to looking through other's solutions.
Thanks again for a fun quiz!
--
(-, /\ \/ / /\/