From: Raj Sahae on 29 Apr 2007 06:43 My second ever rubyquiz submission, so be nice. Sorry if the submission is a little early (by about an hour and a half), but I won't be around a comp tomorrow. Raj Sahae # ccc.rb # Checking Credit Cards # By Raj Sahae class String def begins_with?(str) temp = self.slice(0...str.length) temp == str end end class Array def collect_with_index self.each_with_index do |x, i| self[i] = yield(x, i) end end end class CCNumber #This data was taken from http://en.wikipedia.org/wiki/Credit_card_number TYPES = [ Hash['type' => 'American Express', 'key' => [34, 37], 'length' => [15]], Hash['type' => 'China Union Pay', 'key' => (622126..622925).to_a, 'length' => [16]], Hash['type' => 'Diners Club Carte Blanche', 'key' => (300..305).to_a, 'length' => [14]], Hash['type' => 'Diners Club International', 'key' => [36], 'length' => [14]], Hash['type' => 'Diners Club US & Canada', 'key' => [55], 'length' => [16]], Hash['type' => 'Discover', 'key' => [6011, 65], 'length' => [16]], Hash['type' => 'JCB', 'key' => [35], 'length' => [16]], Hash['type' => 'JCB', 'key' => [1800, 2131], 'length' => [15]], Hash['type' => 'Maestro', 'key' => [5020, 5038, 6759], 'length' => [16]], Hash['type' => 'MasterCard', 'key' => (51..55).to_a, 'length' => [16]], Hash['type' => 'Solo', 'key' => [6334, 6767], 'length' => [16, 18, 19]], Hash['type' => 'Switch', 'key' => [4903, 4905, 4911, 4936, 564182, 633110, 6333, 6759], 'length' => [16, 18, 19]], Hash['type' => 'Visa', 'key' => [4], 'length' => [13, 16]], Hash['type' => 'Visa Electron', 'key' => [417500, 4917, 4913], 'length' => [16]] ] #number should be an array of numbers as strings e.g. ["1", "2", "3"] def initialize(array) @number = array.collect{|num| num.to_i} end def type TYPES.each do |company| company['key'].each do |key| if company['length'].include?(@number.length) and @number.join.begins_with?(key.to_s) return company['type'] end end end "Unknown" end def valid? temp = @number.reverse.collect_with_index{|num, index| index%2 == 0 ? num*2 : num} sum = temp.collect{|num|num > 9 ? [1, num%10] : num}.flatten.inject{|s, n| s+n} sum%10 == 0 end def process puts "The card type is #{self.type}" puts "The card number is #{self.valid? ? 'valid' : 'invalid'}" end end if $0 == __FILE__ abort "You must enter a number!" if ARGV.empty? CCNumber.new(ARGV.join.strip.split(//)).process end
From: anansi on 29 Apr 2007 07:26 Raj Sahae wrote: > My second ever rubyquiz submission, so be nice. > Sorry if the submission is a little early (by about an hour and a half), > but I won't be around a comp tomorrow. > > Raj Sahae > I'll wait with my solution for a further hour ;) but a comment to your solution: try out: ruby ccc.rb 5508 0412 3456 7893 it should show: Diners Club US & Canada or Mastercard but shows just Mastercaards -- greets ( ) ( /\ .-"""-. /\ //\\/ ,,, \//\\ |/\| ,;;;;;, |/\| //\\\;-"""-;///\\ // \/ . \/ \\ (| ,-_| \ | / |_-, |) //`__\.-.-./__`\\ // /.-(() ())-.\ \\ (\ |) '---' (| /) ` (| |) ` jgs \) (/ one must still have chaos in oneself to be able to give birth to a dancing star
From: anansi on 29 Apr 2007 08:00 48 hours are past so here is my first solution. I'm just coding ruby for a few days so any feedback is welcome :) My script recognizes 12 different creditcard companys and of course says if a card could belong to two different companys at the same time or if it is unknown. I think the companycheck part could be done better by calling check.call from a loop but I couldn't figure out how to pass such an hash: hash = Hash.new { |hash, key| hash[key] = [] } raw_data = [ [1,"American Express"],[1,/^34|^37/], [1, "15"], [2,"Diners CLub Blanche"],[2,/^30[0-5]/], [2, "14"], [3,"Solo"],[3,/^6334|^6767/],[3,"16"],[3,"18"],[3,"19"]] raw_data.each { |x,y| hash[x] << y } to check = lambda{|reg,c,*d| if number =~ reg: @company += " or " + c if d.include?(number.length) end } in a loop. Any idea how to do this? here the solution: #!/usr/bin/ruby -w # validate.rb ###################################################### # validator for creditcard numbers # # by anansi # # 29/04/07 on comp.lang.rub # # # # [QUIZ] Checking Credit Cards (#122) # ###################################################### # recognizes: # # American Express, # # Diners CLub Blanche, # # Diners CLub International, # # Diners Club US & Canada, # # Discover, # # JCB, # # Maestro (debit card), # # Mastercard, # # Solo, # # Switch, # # Visa, # # Visa Electron # ###################################################### class CreditCard def initialize(number) number.scan(/\D/) { |x| # scans number for every not-digit symbol puts x + " is no valid credit card symbol.\nJust digits allowed!!" exit } end def companycheck(number) @company= "" # block check compares the length and sets the company value check = lambda{|reg,c,*d| if number =~ reg: @company += " or " + c if d.include?(number.length) end } # adding a new bank is quite easy, just put a new check.call in with: # check.call(regular expressions for the starting bytes,"Company-name",length1,length2,...) # I'm sure this can be done somehow better invoking check.call by a loop # but I couldn't figure out how to pass a array with dynamic variable count into check.call check.call( /^34|^37/ , "American Express" , 15 ) check.call( /^30[0-5]/ ,"Diners CLub Blanche",14) check.call( /^36/ , "Diners CLub International",14) check.call( /^55/ , "Diners Club US & Canada",16) check.call( /^6011|^65/ , "Discover",16) check.call( /^35/ , "JCB" , 16) check.call( /^1800|^2131/ , "JCB" , 15) check.call( /^5020|^5038|^6759/ , "Maestro (debit card)" , 16) check.call( /^5[0-5]/ , "Mastercard" , 16) check.call( /^6334|^6767/ , "Solo" , 16 , 18 , 19) check.call( /^4903|^4905|^4911|^4936|^564182|^633110|^6333|^6759/ , "Switch" , 16 , 18 , 19) check.call( /^4/ , "Visa" , 13 , 16) check.call( /^417500|^4917|^4913/ , "Visa Electron" , 16) if @company == "" puts "Company : Unknown" else puts "Company : #{@company.slice(4..(a)company.length)}" end end def crossfoot(digit) digit = "%2d" % digit # converts integer to string if digit[0] == 32 digit = (digit[1].to_i) -48 else # if the doubled digit has more than 2 digits digit= (digit[0].to_i) + (digit[1].to_i) -96 # adds the single digits and converts back to integer end end def validation(number) math = lambda { |dig| number[@count-dig]-48 } # block math converts str to int of the current digit @duplex = false @count = number.length @result = 0 for i in (1..(a)count) if @duplex == false # for every first digit from the back @result += math.call i # add to result @duplex = true else # for every second digit from the back @result += crossfoot((math.call i)*2) # mutl. digit with 2, do the crossfoot and add to result @duplex = false end end @result.modulo(10) end end #### begin if ARGV.length == 0 # checks if argument is passed puts "no input\nusage, e.g.: ruby validate.rb 4408 0412 3456 7893" exit end number = ARGV.join('').gsub(" ","") # reads args and kills all spaces and newlinefeed my_creditcard = CreditCard.new(number) # checks if just digits are inputed otherwise: abort. my_creditcard.companycheck(number) # checks for known or unknown company if my_creditcard.validation(number) == 0 # checks validation with luhn-algo puts "Validation: successful" else puts "Validation: failure" end ### eof -- greets ( ) ( /\ .-"""-. /\ //\\/ ,,, \//\\ |/\| ,;;;;;, |/\| //\\\;-"""-;///\\ // \/ . \/ \\ (| ,-_| \ | / |_-, |) //`__\.-.-./__`\\ // /.-(() ())-.\ \\ (\ |) '---' (| /) ` (| |) ` jgs \) (/ one must still have chaos in oneself to be able to give birth to a dancing star
From: Harry on 29 Apr 2007 08:03 On 4/27/07, Ruby Quiz <james(a)grayproductions.net> wrote: > > 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. ###### # Check Starting numbers and length def card_ok(str,info) check = "UNKNOWN" info.each do |t| pref = t[0] leng = t[1] name = t[2] pref.each do |x| if x == str.slice(0...x.length) leng.each do |y| if y.to_i == str.length check = name.dup end end end end end return check end # Check Luhn algorithm def luhn(str) luhn_hash = Hash.new("INVALID") if str.length != 0 luhn_hash[0] = "VALID" arr = str.split(//).reverse arr2 = [] arr3 = [] (0...arr.length).each do |u| arr2 << arr[u] if u %2 == 0 arr2 << (arr[u].to_i * 2).to_s if u %2 != 0 end arr2.each do |r| arr3 << r.split(//).inject(0) {|sum,i| sum + i.to_i} end val = arr3.inject(0) {|sum,i| sum + i} % 10 end return luhn_hash[val] end # Card information test = [] test << [["31","34"],["15"],"AMEX"] test << [["6011"],["16"],"DISCOVER"] test << [("51".."55").to_a,["16"],"MASTERCARD"] test << [["4"],["13","16"],"VISA"] #test << [("3528".."3589").to_a,["16"],"JCB"] #test << [[("3000".."3029").to_a + ("3040".."3059").to_a + #("3815".."3889").to_a + ["36","389"]].flatten!,["14"],"DINERS"] # Main str = ARGV.join arr = [] arr << card_ok(str,test) arr << luhn(str) puts arr ###### Harry -- http://www.kakueki.com/ruby/list.html A Look into Japanese Ruby List in English
From: Joseph Seaton on 29 Apr 2007 08:20
My solution (the second I've submitted, so please be nice :). Suggestions welcome. ### BCTYPES = { [[34,37],[15]] => "AMEX", [[6011],[16]] => "Discoverer", [(51..57).to_a,16] => "MasterCard", [[4],[13,16]] => "Visa"} def ctype(num) BCTYPES.each { |n,t| n[0].each { |s| return t if num.grep(/^#{s}/).any? && n[1].include?(num.length) } } "Unknown" end def luhncheck(num) e = false num.split(//).reverse.collect { |a| e=!e a.to_i*(e ? 1:2) }.join.split(//).inject(0) {|a,b| a+b.to_i} % 10 == 0 ? "Valid" : "Invalid" end card = ARGV.join.gsub(/ /, '') if card == "" puts "Usage: #{$0} <card number>" else puts ctype(card) puts luhncheck(card) end ### Joe |