From: akbarhome on
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
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
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
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
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