Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Here Be Dragons

Katrina Owen
September 15, 2013

Here Be Dragons

It's not your fault. Code rots. We don't hold entropy against you, but we expect you to give a damn.

This story is about code that brings new meaning to the word 'legacy'. The accidental discovery of this body of code provoked a moral crisis. I wanted to pretend I hadn’t seen it, yet I couldn't justify tiptoeing quietly away.

This talk examines the dilemmas we face when balancing our choices today with their cost tomorrow.

It's not your fault. Even so, it is your responsibility.

Katrina Owen

September 15, 2013
Tweet

More Decks by Katrina Owen

Other Decks in Programming

Transcript

  1. +

  2. request.form_input"=>#<StringIO:0x007fcbb690f540>, "rack.request.form_hash"=>{"emails"=>&quo [email protected]", "message"=>"", "name"=>"Katrina"}, uot;rack.request.form_vars"=>"[email protected]&message=&name=Katrina", uot;action_dispatch.request.path_parameters"=>{:action=>"share_by_mail_or_sms", :controller=>"greetings 94524"}, "action_controller.instance"=>#<GreetingsController:0x007fcbb7b2ca98

    ...>, uot;action_dispatch.request.request_parameters"=>{"emails"=>"[email protected]", uot;message"=>"", "name"=>"Katrina"}, "rack.request.query_string"=>"&quot uot;rack.request.query_hash"=>{}, "action_dispatch.request.query_parameters"=>{}, "action_dispatch.request quot;emails"=>"[email protected]", "message"=>"", "name"=>"Katrina uot;action"=>"share_by_mail_or_sms", "controller"=>"greetings", "id"=>"34 uot;action_dispatch.request.accepts"=>[*/*], "action_dispatch.request.formats"=>[*/*]}, @fullpath="/tidbit are_by_email_or_sms", @filtered_parameters={"emails"=>"[email protected]", "message"=&g uot;name"=>"Katrina", "action"=>"share_by_mail_or_sms", "controller"=>"gr uot;id"=>"3494524"}, @method="POST">, @_env={"CONTENT_LENGTH"=>"57", uot;CONTENT_TYPE"=>"application/x-www-form-urlencoded", "GATEWAY_INTERFACE"=>"CGI/1.1", uot;PATH_INFO"=>"/tidbits/123/share_by_email_or_sms", "QUERY_STRING"=>"", "REMOTE_ADDR 7.0.0.1", "REMOTE_HOST"=>"localhost", "REQUEST_METHOD"=>"POST", uot;REQUEST_URI"=>"http://gossip.dev/tidbits/123/share_by_email_or_sms", "SCRIPT_NAME"=>"", uot;SERVER_NAME"=>"localhost", "SERVER_PORT"=>"3000", "SERVER_PROTOCOL"=>&quot uot;SERVER_SOFTWARE"=>"WEBrick/1.3.1 (Ruby/1.9.3/2012-04-20)", "HTTP_USER_AGENT"=>"curl/7.21.4 ( rwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5", "HTTP_HOST"=>"gossip.dev", "HTTP_ACCEPT"= uot;rack.version"=>[1, 1], "rack.input"=>#<StringIO:0x007fcbb690f540>, "rack.errors"=>#<I uot;rack.multithread"=>false, "rack.multiprocess"=>false, "rack.run_once"=>false, uot;rack.url_scheme"=>"http", "HTTP_VERSION"=>"HTTP/1.1", "REQUEST_PATH"=>&quo uot;action_dispatch.parameter_filter"=>[:password], "action_dispatch.secret_token"=>" 93f4924fec841d60443d392ff861b0da3ed5d024656a3afd79632dc52355a2aac249ccd32eba784fd61af2dbfc38fcb9ba5f1b6300696de3fc5cb14733198c" uot;action_dispatch.remote_ip"=>127.0.0.1, "rack.session"=>{"session_id"=>"a7e33d9fc77a10921f uot;id"=>"bffb03fdf02a04a13e4c1ef5f2dad949208ae264c248e690"}, "rack.session.options"=>{:path=>&quo uot;, :domain=>nil, :expire_after=>nil, :secure=>false, :httponly=>true, :id=>"a7e33d9fc77a10921f460103c25e38fa& uot;action_dispatch.cookies"=>{}, "action_dispatch.request.unsigned_session_cookie"=> quot;session_id"=>"a7e33d9fc77a10921f460103c25e38fa"}, "action_dispatch.request.content_type"=>applic lencoded, "rack.request.form_input"=>#<StringIO:0x007fcbb690f540>, "rack.request.form_hash"=> quot;emails"=>"[email protected]", "message"=>"", "name"=>"Katrina uot;rack.request.form_vars"=>"[email protected]&message=&name=Katrina", uot;action_dispatch.request.path_parameters"=>{:action=>"share_by_mail_or_sms", :controller=>"greetings 94524"}, "action_controller.instance"=>#<GreetingsController:0x007fcbb7b2ca98 ...>, uot;action_dispatch.request.request_parameters"=>{"emails"=>"[email protected]", uot;message"=>"", "name"=>"Katrina"}, "rack.request.query_string"=>"&quot uot;rack.request.query_hash"=>{}, "action_dispatch.request.query_parameters"=>{}, "action_dispatch.request quot;emails"=>"[email protected]", "message"=>"", "name"=>"Katrina
  3. ENT"=>"curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5", quot;HTTP_HOST"=>"gossip.dev", "HTTP_ACCEPT"=>"*/*", "rack.version"=>[1, 1], quot;rack.input"=>#<StringIO:0x007fcbb6838ae0>,

    "rack.errors"=>#<IO:<STDERR>>, quot;rack.multithread"=>false, "rack.multiprocess"=>false, "rack.run_once"=>false, quot;rack.url_scheme"=>"http", "HTTP_VERSION"=>"HTTP/1.1", "REQUEST_PATH"=>&quo quot;action_dispatch.parameter_filter"=>[:password], "action_dispatch.secret_token"=>" a93f4924fec841d60443d392ff861b0da3ed5d024656a3afd79632dc52355a2aac249ccd32eba784fd61af2dbfc38fcb9ba5f1b6300696de3fc5cb14733198c" quot;action_dispatch.remote_ip"=>127.0.0.1, "rack.session"=>{"session_id"=>"0c69cf043ced70e9ef quot;id"=>"058d52bacce4d0ba74276ce4561d46a65730347c78c11a94", "channel_id"=>176}, "rack.session.o :path=>"/", :domain=>nil, :expire_after=>nil, :secure=>false, :httponly=>true, :id=>" c69cf043ced70e9efd08cb1e7786890"}, "action_dispatch.cookies"=>{}, "action_dispatch.request.unsigned_session_co "session_id"=>"0c69cf043ced70e9efd08cb1e7786890"}, "action_dispatch.request.content_type"=>applic rlencoded, "rack.request.form_input"=>#<StringIO:0x007fcbb6838ae0>, "rack.request.form_hash"=> "emails"=>"[email protected]", "message"=>"", "name"=>"Katrina quot;rack.request.form_vars"=>"[email protected]&message=&name=Katrina", quot;action_dispatch.request.path_parameters"=>{:action=>"share_by_mail_or_sms", :controller=>"greetings 494524"}, "action_controller.instance"=>#<GreetingsController:0x007fcbb7a5bbf0 ...>, quot;action_dispatch.request.request_parameters"=>{"emails"=>"[email protected]", quot;message"=>"", "name"=>"Katrina"}, "rack.request.query_string"=>"&quot quot;rack.request.query_hash"=>{}, "action_dispatch.request.query_parameters"=>{}, "action_dispatch.request "emails"=>"[email protected]", "message"=>"", "name"=>"Katrina quot;action"=>"share_by_mail_or_sms", "controller"=>"greetings", "id"=>"34 quot;greeting"=>#<Greeting id: 3494524, title: "12 år 12.12.12", body: "Hipp hurra for jiinta vår! \r\nGrat ed ...", happens_on: "2012-12-12", created_at: "2012-12-10 08:53:43", updated_at: "2012-12-11 00:00:33& 76, display_mode_id: 5344, created_by: 9845, updated_by: 1, owner_name: "Leif Jørgen Larsen", age: 12, session_hash: nil, s 012-12-10 08:53:43", removed_at: nil, price: 0, kudos_count: 3, about: "Hanne Mari Larsen", uploaded_at: nil, claimed_ omment_count: 0, kind: "birthday", publish_on: nil, view_count: 22, image_uid: "image:apdm.gossip.oa.birthday$20121210 onfirmation_sent_at: "2012-12-10 08:53:43">, "sms_message"=>"Katrina vil at du skal se på en hilsen he is/personalia/greetings/3494524 Sendt fra www.gossip.dev"}, "action_dispatch.request.accepts"=>[*/*], quot;action_dispatch.request.formats"=>[*/*]}, @lookup_context=#<ActionView::LookupContext:0x007fcbb7a5aca0 @details_key=n details={:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml, :haml], :formats=> :html, :text, :js, :css, :ics, :csv, :xml, :rss, :atom, :yaml, :multipart_form, :url_encoded_form, :json], :locale=>[:"no-NB& B"]}, @skip_default_locale=false, @frozen_formats=false, @view_paths=[/Users/katrina/code/apps/gossip/app/views, /Users/katrina/ endor/plugins/papertime_client/app/views, /Users/katrina/code/apps/gossip/vendor/plugins/origo_client/app/views, /Users/katrina/code/ lugins/model_bars/app/views, /Users/katrina/code/apps/gossip/vendor/plugins/crummy/app/views, /Users/katrina/code/apps/gossip/vendor/ pdm_stationary_client/app/views, /Users/katrina/code/apps/gossip/vendor/plugins/acts_as_tsearch/app/views, /Users/katrina/code/apps/g ctive_merchant/app/views, /Users/katrina/.rvm/gems/ruby-1.9.3-p194@gossip/gems/kaminari-0.13.0/app/views]>, _action_name="share_by_mail_or_sms", @_response_body=nil, @crumbtrail=[{:title=>"Forsiden", :url=>"htt ww.gossip.dev"}, {:title=>"Folk i dag", :url=>"/vis/personalia/greetings"}], @_config={}, @current_cha 76, name: "Oppland Arbeiderblad", home_page: "http://www.gossip.dev", created_at: "2010-09-22 08:33:43"
  4. ENT"=>"curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5", quot;HTTP_HOST"=>"gossip.dev", "HTTP_ACCEPT"=>"*/*", "rack.version"=>[1, 1], quot;rack.input"=>#<StringIO:0x007fcbb6838ae0>,

    "rack.errors"=>#<IO:<STDERR>>, quot;rack.multithread"=>false, "rack.multiprocess"=>false, "rack.run_once"=>false, quot;rack.url_scheme"=>"http", "HTTP_VERSION"=>"HTTP/1.1", "REQUEST_PATH"=>&quo quot;action_dispatch.parameter_filter"=>[:password], "action_dispatch.secret_token"=>" a93f4924fec841d60443d392ff861b0da3ed5d024656a3afd79632dc52355a2aac249ccd32eba784fd61af2dbfc38fcb9ba5f1b6300696de3fc5cb14733198c" quot;action_dispatch.remote_ip"=>127.0.0.1, "rack.session"=>{"session_id"=>"0c69cf043ced70e9ef quot;id"=>"058d52bacce4d0ba74276ce4561d46a65730347c78c11a94", "channel_id"=>176}, "rack.session.o :path=>"/", :domain=>nil, :expire_after=>nil, :secure=>false, :httponly=>true, :id=>" c69cf043ced70e9efd08cb1e7786890"}, "action_dispatch.cookies"=>{}, "action_dispatch.request.unsigned_session_co "session_id"=>"0c69cf043ced70e9efd08cb1e7786890"}, "action_dispatch.request.content_type"=>applic rlencoded, "rack.request.form_input"=>#<StringIO:0x007fcbb6838ae0>, "rack.request.form_hash"=> "emails"=>"[email protected]", "message"=>"", "name"=>"Katrina quot;rack.request.form_vars"=>"[email protected]&message=&name=Katrina", quot;action_dispatch.request.path_parameters"=>{:action=>"share_by_mail_or_sms", :controller=>"greetings 494524"}, "action_controller.instance"=>#<GreetingsController:0x007fcbb7a5bbf0 ...>, quot;action_dispatch.request.request_parameters"=>{"emails"=>"[email protected]", quot;message"=>"", "name"=>"Katrina"}, "rack.request.query_string"=>"&quot quot;rack.request.query_hash"=>{}, "action_dispatch.request.query_parameters"=>{}, "action_dispatch.request "emails"=>"[email protected]", "message"=>"", "name"=>"Katrina quot;action"=>"share_by_mail_or_sms", "controller"=>"greetings", "id"=>"34 quot;greeting"=>#<Greeting id: 3494524, title: "12 år 12.12.12", body: "Hipp hurra for jiinta vår! \r\nGrat ed ...", happens_on: "2012-12-12", created_at: "2012-12-10 08:53:43", updated_at: "2012-12-11 00:00:33& 76, display_mode_id: 5344, created_by: 9845, updated_by: 1, owner_name: "Leif Jørgen Larsen", age: 12, session_hash: nil, s 012-12-10 08:53:43", removed_at: nil, price: 0, kudos_count: 3, about: "Hanne Mari Larsen", uploaded_at: nil, claimed_ omment_count: 0, kind: "birthday", publish_on: nil, view_count: 22, image_uid: "image:apdm.gossip.oa.birthday$20121210 onfirmation_sent_at: "2012-12-10 08:53:43">, "sms_message"=>"Katrina vil at du skal se på en hilsen he is/personalia/greetings/3494524 Sendt fra www.gossip.dev"}, "action_dispatch.request.accepts"=>[*/*], quot;action_dispatch.request.formats"=>[*/*]}, @lookup_context=#<ActionView::LookupContext:0x007fcbb7a5aca0 @details_key=n details={:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml, :haml], :formats=> :html, :text, :js, :css, :ics, :csv, :xml, :rss, :atom, :yaml, :multipart_form, :url_encoded_form, :json], :locale=>[:"no-NB& B"]}, @skip_default_locale=false, @frozen_formats=false, @view_paths=[/Users/katrina/code/apps/gossip/app/views, /Users/katrina/ endor/plugins/papertime_client/app/views, /Users/katrina/code/apps/gossip/vendor/plugins/origo_client/app/views, /Users/katrina/code/ lugins/model_bars/app/views, /Users/katrina/code/apps/gossip/vendor/plugins/crummy/app/views, /Users/katrina/code/apps/gossip/vendor/ pdm_stationary_client/app/views, /Users/katrina/code/apps/gossip/vendor/plugins/acts_as_tsearch/app/views, /Users/katrina/code/apps/g ctive_merchant/app/views, /Users/katrina/.rvm/gems/ruby-1.9.3-p194@gossip/gems/kaminari-0.13.0/app/views]>, _action_name="share_by_mail_or_sms", @_response_body=nil, @crumbtrail=[{:title=>"Forsiden", :url=>"htt ww.gossip.dev"}, {:title=>"Folk i dag", :url=>"/vis/personalia/greetings"}], @_config={}, @current_cha 76, name: "Oppland Arbeiderblad", home_page: "http://www.gossip.dev", created_at: "2010-09-22 08:33:43"
  5. i18n missing interpolation argument in "%{tidbit_sender} has congratulated %{tidbit_about} in

    %{publication}. %{tip_sender} wants you to take a look." ( { :tip_sender=>"Katrina", :tidbit_about=>"Eve Smith", :publication=>"The Village Rag" } given)
  6. Error Message missing interpolation argument in "%{tidbit_sender} has congratulated %{tidbit_about}

    in %{publication}. %{tip_sender} wants you to take a look." ( { :tip_sender=>"Katrina", :tidbit_about=>"Eve Smith", :publication=>"The Village Rag" } given)
  7. missing interpolation argument in "%{tidbit_sender} has congratulated %{tidbit_about} in %{publication}.

    %{tip_sender} wants you to take a look." ( { :tip_sender=>"Katrina", :tidbit_about=>"Eve Smith", :publication=>"The Village Rag" } given) Error Message
  8. missing interpolation argument in "%{tidbit_sender} has congratulated %{tidbit_about} in %{publication}.

    %{tip_sender} wants you to take a look." ( { :tip_sender=>"Katrina", :tidbit_about=>"Eve Smith", :publication=>"The Village Rag" } given) Error Message
  9. missing interpolation argument in "%{tidbit_sender} has congratulated %{tidbit_about} in %{publication}.

    %{tip_sender} wants you to take a look." ( { :tip_sender=>"Katrina", :tidbit_about=>"Eve Smith", :publication=>"The Village Rag" } given) Error Message
  10. missing interpolation argument in "%{tidbit_sender} has congratulated %{tidbit_about} in %{publication}.

    %{tip_sender} wants you to take a look." ( { :tip_sender=>"Katrina", :tidbit_about=>"Eve Smith", :publication=>"The Village Rag" } given) Error Message
  11. missing interpolation argument in "%{tidbit_sender} has congratulated %{tidbit_about} in %{publication}.

    %{tip_sender} wants you to take a look." ( { :tip_sender=>"Katrina", :tidbit_about=>"Eve Smith", :publication=>"The Village Rag" } given) Error Message
  12. missing interpolation argument in "%{tidbit_sender} has congratulated %{tidbit_about} in %{publication}.

    %{tip_sender} wants you to take a look." ( { :tip_sender=>"Katrina", :tidbit_about=>"Eve Smith", :publication=>"The Village Rag" } given) Error Message
  13. <%= t("emails.tip_mail.introduction") % { :tip_sender => @tip_sender, :tidbit_about => @tidbit.about,

    :publication => @tidbit.publication :tidbit_sender => @tidbit.from } %> template
  14. def self.lookup_tiny_url(url) # ... uri = URI.parse(endpoint) Net::HTTP.start(uri.host, uri.port) do

    |api| api.get("#{uri.path}?#{uri.query}") end # ... end def self.endpoint "http://is.gd/create.php" end module
  15. def self.should_lookup_tiny_url? return false if cached? case env when "production"

    then return true when "staging" then return true else return false end false end module
  16. def self.should_lookup_tiny_url? return false if cached? case env when "production"

    then return true when "staging" then return true else return false end false end module
  17. module def self.should_lookup_tiny_url? return false if cached? case env when

    "production" then return true when "staging" then return true else return false end false end
  18. module def self.should_lookup_tiny_url? return false if cached? case env when

    "production" then return true when "staging" then return true else return false end false end
  19. @@lookup_tiny_urls = {} def self.shorten(url) @url = url # ...

    end def self.cached? !!@@lookup_tiny_urls[@url] end module
  20. xp 066 name = params[:name] if name && name.length >

    40 invalid_name = true else # ... end controller
  21. xp 070 name = params[:name] if name && name.length >

    40 invalid_name = true else # ... end controller
  22. xp NAME=$(ruby -e "40.times { print 'x' }") curl -XPOST

    $URL \ --data "emails=$EMAILS&name=$NAME" 070
  23. xp if name && name.length > 40 # ... end

    Gossip::Sharing.send_to_recipients(params) controller 134
  24. xp controller if name && name.length > 40 # ...

    end Gossip::Sharing.send_to_recipients(params) unless invalid_name # ... else halt 400, {error: "Invalid name."}.to_json end 134
  25. xp name = params[:name] if name && name.length > 40

    invalid_name = true end controller 134
  26. xp # Sends a params[:message] to a set of #

    params[:email_recipients] and # params[:sms_recipients] def self.send_to_recipients(params) # ... end module 142
  27. xp # Sends a params[:message] to a set of #

    params[:email_recipients] and # params[:sms_recipients] def self.send_to_recipients(params) # ... end module 142
  28. xp 142 # Sends a params[:sms] to a set of

    # params[:email_recipients] and # params[:sms_recipients] def self.send_to_recipients(params) # ... end module
  29. xp 144 # Sends a params[:sms] to a set of

    # params[:email_recipients] and # params[:sms_recipients] def self.send_to_recipients(params) # ... end module
  30. xp # Sends a params[:sms] to a set of #

    params[:email_recipients] and # params[:sms_recipients] def self.send_to_recipients(params) # ... end 144 module
  31. xp 144 # Sends a params[:sms] to a set of

    # params[:email] and # params[:sms_recipients] def self.send_to_recipients(params) # ... end module
  32. xp 146 # Sends a params[:sms] to a set of

    # params[:email] and # params[:sms_recipients] def self.send_to_recipients(params) # ... end module
  33. xp # Sends a params[:sms] to a set of #

    params[:email] and # params[:sms_recipients] def self.send_to_recipients(params) # ... end 146 module
  34. xp 146 # Sends a params[:sms] to a set of

    # params[:email] and # params[:numbers] def self.send_to_recipients(params) # ... end module
  35. xp 148 # Sends a params[:sms] to a set of

    # params[:email] and # params[:numbers] def self.send_to_recipients(params) # ... end module
  36. xp # Sends a params[:sms] to a set of #

    params[:email] and params[:numbers] def self.send_to_recipients(params) # ... end 148 module
  37. xp 148 # Sends a params[:sms] to a set of

    # params[:email] and params[:numbers] def self.send_to_recipients(params) # ... end module
  38. xp 150 # Sends a params[:sms] to a set of

    # params[:email] and params[:numbers] def self.send_to_recipients(params) # ... end module
  39. xp # Sends params[:sms] to params[:numbers] # and a templated

    email to params[:email] def self.send_to_recipients(params) # ... end module 150
  40. xp recipients = [emails, phone_numbers].flatten recipients.each do |r| # lots

    of stuff unless stuff? if r =~ /^([^@\s,]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i send_to_email(r, params) elsif r =~ /^\s*\+?[0-9[:space:]]+\s*$/ send_to_sms(r, params) end # more stuff end end module 161
  41. xp recipients = [emails, phone_numbers].flatten recipients.each do |r| if r

    =~ /^([^@\s,]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i elsif r =~ /^\s*\+?[0-9[:space:]]+\s*$/ end end module 289
  42. xp recipients = [emails, phone_numbers].flatten recipients.each do |r| if r

    =~ /^([^@\s,]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i send_to_email(r, params) elsif r =~ /^\s*\+?[0-9[:space:]]+\s*$/ end end module 289
  43. xp recipients = [emails, phone_numbers].flatten recipients.each do |r| if r

    =~ /^([^@\s,]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i send_to_email(r, params) elsif r =~ /^\s*\+?[0-9[:space:]]+\s*$/ send_to_sms(r, params) end end module 289
  44. xp if r =~ /^([^@\s,]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i send_to_email(r, params) elsif r =~

    /^\s*\+?[0-9[:space:]]+\s*$/ send_to_sms(r, params) end module 289
  45. xp module Alice has a baby and posts a picture.

    Bob tells Charlie about it Bob provides Charlie’s email 289
  46. xp Story Alice has a baby and posts a picture.

    Bob tells Charlie about it Bob provides Charlie’s email 289
  47. xp recipients = [emails, phone_numbers].flatten recipients.each do |r| # lots

    of stuff unless stuff? if r =~ /^([^@\s,]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i send_to_email(r, params) elsif r =~ /^\s*\+?[0-9[:space:]]+\s*$/ send_to_sms(r, params) end # more stuff end end module 161
  48. xp sent = [] recipients = [emails, phone_numbers].flatten recipients.each do

    |r| # lots of stuff unless stuff? if r =~ /^([^@\s,]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i if send_to_email(r, params) sent << r end elsif r =~ /^\s*\+?[0-9[:space:]]+\s*$/ if send_to_sms(r, params) sent << r end end # more stuff end end sent module 161
  49. xp if r =~ /^([^@\s,]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i send_to_email(r, params) elsif r =~

    /^\s*\+?[0-9[:space:]]+\s*$/ send_to_sms(r, params) end module 305
  50. Credits Source Code Anonymous Pixel Bug Illustrations Persa Zula The

    Evolution of Cooperation Robert Axelrod The Prisoner’s Dilemma Game Theory 101