I was trying to go for "most compact and obfuscated version of the
Luhn algorithm", but unfortunately the complications of the algorithm
meant that I had to debug my implementation, which left it slightly
more readable.  Oh well.  It's still pretty dense and while my type
method might be readable it's as dense as I care to make it.

#!ruby -x
def type(s)
    case s.gsub(/\D/,'')
    when /^(?=34).{15}$/; "AMEX"
    when /^(?=37).{15}$/; "AMEX"
    when /^(?=6011).{16}$/; "Discover"
    when /^(?=5[1-5]).{16}$/; "MasterCard"
    when /^(?=4).{13}(...)?$/; "Visa"
    else ; "Unknown"
    end
end

def luhn(s)
  s.scan(/\d/).inject([0,0]){|(a,b),c|[b+c.to_i,
  a+c.to_i*2%9+(c=='9' ? 9 : 0)]}[0]%10 == 0
end

s = ARGV.join
puts "#{type(s)} #{luhn(s)?'V':'Inv'}alid"

__END__
-- 
s=%q(  Daniel Martin -- martin / snowplow.org
       puts "s=%q(#{s})",s.to_a[1]       )
       puts "s=%q(#{s})",s.to_a[1]