From: akbarhome on 30 Apr 2007 00:22 On Apr 27, 6:59 pm, Ruby Quiz <j...(a)grayproductions.net> wrote: > 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.rubyquiz.com/ > > 3. Enjoy! > > Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone > on Ruby Talk follow the discussion. Please reply to the original quiz message, > if you can. > > -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- =-=-=-=-= > > Before a credit card is submitted to a financial institution, it generally makes > sense to run some simple reality checks on the number. The numbers are a good > length and it's common to make minor transcription errors when the card is not > scanned directly. > > The first check people often do is to validate that the card matches a known > pattern from one of the accepted card providers. Some of these patterns are: > > +============+=============+===============+ > | Card Type | Begins With | Number Length | > +============+=============+===============+ > | AMEX | 34 or 37 | 15 | > +------------+-------------+---------------+ > | Discover | 6011 | 16 | > +------------+-------------+---------------+ > | MasterCard | 51-55 | 16 | > +------------+-------------+---------------+ > | Visa | 4 | 13 or 16 | > +------------+-------------+---------------+ > > All of these card types also generate numbers such that they can be validated by > the Luhn algorithm, so that's the second check systems usually try. The steps > are: > > 1. Starting with the next to last digit and continuing with every other > digit going back to the beginning of the card, double the digit > 2. Sum all doubled and untouched digits in the number > 3. If that total is a multiple of 10, the number is valid > > For example, given the card number 4408 0412 3456 7893: > > Step 1: 8 4 0 8 0 4 2 2 6 4 10 6 14 8 18 3 > Step 2: 8+4+0+8+0+4+2+2+6+4+1+0+6+1+4+8+1+8+3 = 70 > Step 3: 70 % 10 == 0 > > Thus that card is valid. > > Let's try one more, 4417 1234 5678 9112: > > Step 1: 8 4 2 7 2 2 6 4 10 6 14 8 18 1 2 2 > Step 2: 8+4+2+7+2+2+6+4+1+0+6+1+4+8+1+8+1+2+2 = 69 > Step 3: 69 % 10 != 0 > > That card is not valid. > > This week's Ruby Quiz is to write a program that accepts a credit card number as > a command-line argument. The program should print the card's type (or Unknown) > as well a Valid/Invalid indication of whether or not the card passes the Luhn > algorithm. Here is my solution. #!/usr/bin/ruby credit_card_number = ARGV.join case when (credit_card_number=~/^(34|37)\d{13}$/): print 'AMEX ' when (credit_card_number=~/^6011\d{12}$/): print 'Discover ' when (credit_card_number=~/^5[1-5]\d{14}$/): print 'MasterCard ' when (credit_card_number=~/^4(\d{12}|\d{15})$/): print 'Visa ' else print 'Unknown ' end i = 0 luhl_number = '' credit_card_number.reverse.each_byte {|char| if (i%2==1) then char = (char.chr.to_i * 2).to_s else char = char.chr end luhl_number = char + luhl_number i += 1 } sum_total = 0 luhl_number.each_byte {|char| sum_total += char.chr.to_i } if (sum_total%10==0) then print "Valid\n" else print "Invalid\n" end
From: Drew Olson on 30 Apr 2007 11:08 My solution is below. # file: credit_card.rb # author: Drew Olson class CreditCard def initialize num @number = num end # check specified conditions to determine the type of card def type length = @number.size if length == 15 && @number =~ /^(34|37)/ "AMEX" elsif length == 16 && @number =~ /^6011/ "Discover" elsif length == 16 && @number =~ /^5[1-5]/ "MasterCard" elsif (length == 13 || length == 16) && @number =~ /^4/ "Visa" else "Unknown" end end # determine if card is valid based on Luhn algorithm def valid? digits = '' # double every other number starting with the next to last # and working backwards @number.split('').reverse.each_with_index do |d,i| digits += d if i%2 == 0 digits += (d.to_i*2).to_s if i%2 == 1 end # sum the resulting digits, mod with ten, check against 0 digits.split('').inject(0){|sum,d| sum+d.to_i}%10 == 0 end end if __FILE__ == $0 card = CreditCard.new(ARGV.join.chomp) puts "Card Type: #{card.type}" if card.valid? puts "Valid Card" else puts "Invalid Card" end end -- Posted via http://www.ruby-forum.com/.
From: Matthew Moss on 30 Apr 2007 16:08 Here is my version... Note that it is probably incorrect w.r.t. the valid/invalid and known/unknown issue discussed earlier... However, after finishing my initial version, and despite how easy it would be to fix, I am pretty swamped with other tasks and so won't fix it. class Integer def digitSum self.to_s.split(//).inject(0) { |s, d| s + d.to_i } end end class CreditCard TYPE_MATCH = { :visa => /^4/, :discover => /^6011/, :amex => /^3(4|7)/, :mastercard => /^5[1-5]/ } SIZE_MATCH = { :visa => [13, 16], :discover => [16], :amex => [15], :mastercard => [16] } CARD_NAME = { :visa => "Visa", :discover => "Discover", :amex => "American Express", :mastercard => "MasterCard", :unknown => "unknown", nil => "invalid" } def initialize(cc) @cc = cc.delete(" ") end def valid? self.type && (self._luhn % 10).zero? end def type TYPE_MATCH.each do |k, v| if @cc =~ v if SIZE_MATCH[k].find { |n| @cc.size == n } return k else return nil end end end :unknown end def name CARD_NAME[self.type] end def to_s @cc end def _luhn sum = 0 cc = @cc.split(//) until cc.empty? a, b = cc.pop.to_i, cc.pop.to_i sum += a + (2 * b).digitSum end sum end end cc = CreditCard.new(ARGV.join) if !cc.valid? puts "Card #{cc} is invalid." elsif cc.type != :unknown puts "Card #{cc} is a valid #{cc.name}." else puts "Card #{cc} is unknown type, may be valid." end
From: Bob Lisbonne on 1 May 2007 21:17 My second ever Ruby Quiz. TIA for any suggestions for making it more Ruby-like. /Bob #!/usr/bin/env ruby -w class CreditCard attr_reader :number, :type, :validity def initialize(cardnumber) @number = cardnumber.gsub(/\s/,'') @type = case @number when /^3[47]\d{13}$/ then "AMEX" when /^6011\d{12}$/ then "Discover" when /^5[12345]\d{14}$/ then "Mastercard" when /^4\d{12}$/ then "VISA" when /^4\d{15}$/ then "VISA" else "Unknown" end sum = 0 digits = @number.to_s.split('').reverse.map {|i| i.to_i} digits.each_index {|i| i%2==0 ? sum+=add_digits(digits[i]) : sum +=add_digits(digits[i]*2)} @validity = sum%10 == 0 ? "Valid" : "Invalid" end def add_digits(n) return n.to_s.split('').inject(0) {|sum, i| sum += i.to_i} end end #CreditCard c = CreditCard.new(ARGV.join) puts "#{c.number}: #{c.type}\t#{c.validity}"
From: James Edward Gray II on 2 May 2007 08:04
On Apr 29, 2007, at 1:17 PM, Brad Ediger wrote: > OK, this is the first Quiz contribution I have made (publicly). Wow, a semi-local solving the quiz. I guess that means you need to get back to another OK.rb meeting Brad. ;) Welcome to all the new solvers! James Edward Gray II |