Long distance random assignment

Alec Jacobson

August 13, 2009

weblog/

I am moving into a new apartment and we are trying to figure out who gets which room (one is worse than the rest so obviously nobody wants that room). There are four of us and we are not all in the same physical location. If we were all together we could draw straws or use some equally random event to decide an ordering to see who picks first and so forth. Being separated by distance the problem is complicated. It's like playing rock, scissors over the phone. If you blurt out your objects at the same time you can't hear the other person. If there is a delay then the delay will favor one person or at least put one person in a difficult ethical position. If I'm planning to call 'rock' and after hearing the other person call 'paper' I have enough time to change my call to 'scissors', should I (1) accept that I am going to lose and keep my original call or (2) change my call and win. Obviously the ethical choice is (1) not to change. The problem is not which decision is ethical, rather the problem is that somebody ever had to make that ethical decision. A truly random assignment shouldn't involve an ethical decision, or any decision. My solution was a web app (ruby code below). Given a list of email addresses, the app randomly orders the email addresses and then emails the same random ordering to each of the addresses. Each of the recipients get the same email with the same random ordering. Each will know that the ordering was only declared once. There is no faith on any of the participants to act ethically which means that it returns the situation to an event as random and ethically neutral as drawing straws in person. Here's the code:
#!/usr/bin/ruby

require 'cgi'
require 'net/smtp'
EMAIL_REGEX = Regexp.new(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i)

def send_email(from, from_alias, to, subject, message)
  msg = <<END_OF_MESSAGE
From: #{from_alias} <#{from}>
To: #{to.join(", ")}
Subject: #{subject}

#{message}
END_OF_MESSAGE

        Net::SMTP.start('localhost') do |smtp|
                smtp.send_message msg, from, to
        end
end

cgi = CGI.new("html3")  # add HTML generation methods
cgi.out{
  cgi.html{
    cgi.body{
      response = ""
      if(cgi['addresses'].empty?)
        # no addresses given
      else
        addresses = cgi['addresses'].split(/[,\s]+/) 
        # check that each address is proper
        errors = []
        addresses.each do |address|
          if(address !~ EMAIL_REGEX)
             errors << "\"#{address}\" is not a proper email address"
          end
        end
        if(errors.empty?)
          sorted_addresses = addresses.sort_by{rand}
          index = 0
          message = sorted_addresses.collect do |address| 
            "#{index+=1}: #{address}"
          end.join("\n") 
          send_email(
            "no-reply@randomly-order-emails",
            "randomly-order-emails",
            addresses,
            "Email Addresses Sorted by Random",
            "\n"+message)
          response = cgi.div(
            "style"=>"background:#4F5;padding:5px;color:#000;font-family:sans-serif;"
          ){
            "<h3>Randomly ordered addresses sent to:</h3>"+
            addresses.join("<br>")+
            "<br>"+
            "<br>"+
            cgi.div(
              "style"=>"font-size:0.62em"
            ){"*The above ordering is the original order you just "+
               "submitted, the random ordering is arriving to your inboxes"
            }
          }
        else
          response = cgi.div(
            "style"=>"background:#F54;padding:5px;color:#000;font-family:sans-serif;"
          ){
            errors.map{|error| "<strong>Error:</strong> "+error}.join("</br>")
          }
        end
      end
      response + 
      cgi.div{
        cgi.form(
          "action"=>"./index.cgi",
          "onSubmit"=>"replace_commas()",
          "name"=>"form"
        ){
          "\n"+
          cgi.hr +
          cgi.h3{"Enter email addresses to be randomly ordered:"}+
          "\n"+
          cgi.textarea("name"=>"addresses", "rows"=>"10", "cols"=>"50"){
            cgi['addresses'].split(/[,\s]+/).join(", ")
          } +"\n"+
          cgi.br +
          cgi.submit
        }
    }
  }
}
Most of the code is just setting up the html, the post form and sending the emails. To reorder a list or array randomly in ruby is particularly simple. If my_array = ["a@d.com","b@d.com","c@d.com"], then I can simply call sort_by{rand} like this:
  randomly_ordered_array = my_array.sort_by{rand}