[SOLUTION] [QUIZ] Checking Credit Cards (#122)

Discussion in 'Ruby' started by Todd Benson, Apr 29, 2007.

  1. Todd Benson

    Todd Benson Guest

    Spartan solution (along with its obscure sister below)
    :)


    # usage: ruby quiz122.rb <credit card number with no
    dashes or spaces>
    # example: ruby quiz122.rb 341275084937123

    # some variables
    user_input = $*[0]
    sum_is_valid = card_is_known = false
    sum = 0

    # the limited database
    db = {
    3.4*10**14..3.5*10**14-1 => "AMEX",
    3.7*10**14..3.8*10**14-1 => "AMEX",
    6.011*10**15..6.012*10**15-1 => "Discover",
    5.1*10**15..5.6*10**15-1 => "MasterCard",
    4*10**12..5*10**12-1 => "Visa",
    4*10**15..5*10**15-1 => "Visa",
    }

    # check the database
    number = user_input.to_i
    type = 'unknown'
    db.each { |key,value| type = value if key === number }
    card_is_known ||= type != 'unknown'

    # ugly way to code the Luhn algorithm
    #
    # 1. initialize sum
    # 2. reverse the supplied string
    # 3. turn the string into an array with one digit per
    index
    # 4. turn the individual digits into integers
    # 5. sum everything up ...
    # 5a. add the digit to the sum if the array index is
    odd
    # 5b. add the digits making up twice the value of the
    digit if the array index is even
    # 6. test 2 is valid if the sum is divisible by 10

    (user_input.reverse.scan(/\d/).map! { |digit|
    digit.to_i }).each_with_index { |digit,index| sum += (
    index % 2 == 0 ? digit : digit.divmod(5)[1] * 2 +
    digit.divmod(5)[0] ) }
    puts sum
    sum_is_valid ||= sum % 10 == 0

    # print results
    card_is_known = (card_is_known ? "is" : "is not")
    sum_is_valid = (sum_is_valid ? "is" : "is not")
    puts "The card #{card_is_known} known."
    puts "The card type is #{type}."
    puts "The card number sum #{sum_is_valid} valid."

    --------------------------------------------------

    Solution, obscure version :)

    # preliminary data
    sum=known=false;type='';t=0
    db = {
    3.4*10**14..3.5*10**14-1 => "AMEX",
    3.7*10**14..3.8*10**14-1 => "AMEX",
    6.011*10**15..6.012*10**15-1 => "Discover",
    5.1*10**15..5.6*10**15-1 => "MasterCard",
    4*10**12..5*10**12-1 => "Visa",
    4*10**15..5*10**15-1 => "Visa",
    }

    # check stuff
    db.each { |k,v| type=v if
    k===$*[0].to_i};known||=type!=''
    ($*[0].reverse.scan(/\d/).map!{|d|d.to_i}).each_with_index{|d,i|t+=(i%2==0
    ? d : d.divmod(5)[1]*2+d.divmod(5)[0])};sum||=t%10==0

    # cryptically print results
    puts "Known: #{known}\nType: #{type}\nValid sum: #{sum}"

    __________________________________________________
    Do You Yahoo!?
    Tired of spam? Yahoo! Mail has the best spam protection around
    http://mail.yahoo.com
    Todd Benson, Apr 29, 2007
    #1
    1. Advertising

  2. Hi all,
    Here's my solution...

    (pastie: http://pastie.caboo.se/57591)

    regards,
    rolando.-

    #!/usr/bin/env ruby

    # RubyQuiz #122
    # Solution by Rolando Abarca M.
    # rabarca (at) scio.cl

    # small hack to allow intervals as a cc length
    class Fixnum
    def include?(n)
    self == n
    end
    end

    module CChecker
    # prefixes taken from wikipedia
    # http://en.wikipedia.org/wiki/Credit_card_number
    PREFIXES = [
    # regexp, length, name, checking algorithm
    # length can be a fixnum, array or range.
    # the algorithm must be in the CChecker module
    [/^(34|37)\d+$/, 15, "AMEX", :luhn],
    [/^30[0-5]\d+$/, 14, "Diners Club Carte Blanche", :luhn],
    [/^36\d+$/, 14, "Diners Club International", :luhn],
    [/^55\d+$/, 16, "Diners Club US & Canada", :luhn],
    [/^(6011|65)\d+$/, 16, "Discover", :luhn],
    [/^35\d+$/, 16, "JCB", :luhn],
    [/^(1800|2131)\d+$/, 15, "JCB", :luhn],
    [/^(5020|5038|6759)\d+$/, 16, "Maestro", :luhn],
    [/^(51|54|55)\d+$/, 16, "Mastercard", :luhn],
    [/^(6334|6767)\d+$/, [16,19], "Solo", :luhn],
    [/^4\d+$/, [13,16], "Visa", :luhn],
    [/^(417500|4917|4913)\d+$/, 16, "Visa Electron", :luhn]
    ]
    UNKNOWN_PREFIX = [nil, 0, "Unknown", :luhn]

    def CChecker.usage(doexit = false)
    puts "usage: cchecker.rb <ccnumber>"
    exit if doexit
    end

    # try to identify the card
    def CChecker.check_prefix(ccnumber)
    pr = PREFIXES.detect {|p| p[0].match(ccnumber) &&
    p[1].include?(ccnumber.length)}
    (pr.nil?) ? UNKNOWN_PREFIX : pr
    end

    # do the complete check of the cc:
    # 1.- try to identify
    # 2.- apply algorithm (should return true/false)
    # returns an array: [isvalid, card_identifier]
    def CChecker.check(ccnumber)
    ccnumber = ccnumber.to_s.delete(" ")
    pr = check_prefix(ccnumber)
    [send(pr[3], ccnumber), pr[2]]
    end

    # classic Luhn's algorithm
    def CChecker.luhn(ccnumber)
    sum = 0
    ccnumber.reverse.split(//).each_with_index do |c, i|
    cx = c[0]-48; # this should be faster than c.to_i, right?
    next if cx > 9 || cx < 0 # only numbers, please
    if (i+1) & 1 == 0
    cx *= 2
    cx = (cx/10 + cx%10) if cx > 9
    end
    sum += cx
    end
    sum % 10 == 0
    end
    end

    CChecker::usage(true) if ARGV.size != 1
    valid, card = CChecker::check(ARGV[0])
    if valid
    puts "#{card} Valid"
    else
    puts "#{card} Invalid"
    end
    Rolando Abarca, Apr 30, 2007
    #2
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ruby Quiz
    Replies:
    57
    Views:
    505
    Paul Novak
    May 3, 2007
  2. Daniel Martin
    Replies:
    4
    Views:
    160
  3. Brian Krahmer
    Replies:
    0
    Views:
    111
    Brian Krahmer
    May 1, 2007
  4. Pieter V.
    Replies:
    0
    Views:
    107
    Pieter V.
    May 3, 2007
  5. Ruby Quiz
    Replies:
    17
    Views:
    192
    Robert Dober
    May 6, 2007
Loading...

Share This Page