I *finally* had a chance to play with this.  On the premise of "better
late than never"...

My solution is pretty bare-bones, but uses regular expressions to
"simplify" the deck manipulations.  I didn't keep the suit/rank notation
of the description except for the jokers being A and B.  The rest of the
cards are simply their numeric values.  Of course this means that the
"deck" string contains a newline, so all regular expressions involving a
'.' have to be multiline.




module SolitaireCipher 
  class Keystream 
    ORDERED_DECK = (("\000".."\063").to_a + ['A','B']).join 

    def initialize(deck = ORDERED_DECK.dup) 
      @deck = deck 
      @original_deck = @deck.dup 
    end 

    def reset 
      @deck = @original_deck.dup 
      self 
    end 

    def next_key 
      case @deck 
        when /A\z/ then @deck.sub!(/(.*)A/m,'A\1') 
        else            @deck.sub!(/A(.)/m,'\1A') 
      end 
      case @deck 
        when /B\z/   then @deck.sub!(/(..)(.*)B/m,'\1B\2') 
        when /B.\z/m then @deck.sub!(/(.)(.*)B/m,'\1B\2') 
        else              @deck.sub!(/B(..)/m,'\1B') 
      end 
      @deck.sub!(/(.*)([AB].*[AB])(.*)/m,'\3\2\1') 
      count = if @deck =~ /[AB]\z/ then 53 else @deck[-1] + 1 end 
      @deck.sub!(/(.{#{count}})(.*)(.)/m,'\2\1\3') 
      count = if @deck =~ /\A[AB]/ then 53 else @deck[0] + 1 end 
      if @deck[count..count] =~ /[AB]/ then next_key else @deck[count] +
1 end 
    end 
  end 

  def self.text2intarray(text) 
    intarray = [] 
    text.upcase.delete('^A-Z').each_byte { |letter| intarray << (letter
- ?A) } 
    intarray.fill((?X - ?A), intarray.length, (-intarray.length) % 5) 
  end 

  def self.intarray2text(intarray) 
    text = '' 
    intarray.each { |int| text += (int + ?A).chr } 
    text.gsub(/(.....)(?=.)/,'\1 ') 
  end 

  def self.encode(plaintext,keystream) 
    intarray2text(text2intarray(plaintext).map { |int| (int +
keystream.next_key) % 26 }) 
  end 

  def self.decode(ciphertext,keystream) 
    intarray2text(text2intarray(ciphertext).map { |int| (int -
keystream.next_key) % 26 }) 
  end 
end 

keystream = SolitaireCipher::Keystream.new 
puts ciphertext = SolitaireCipher.encode("Code in Ruby, live
longer!",keystream) 
puts SolitaireCipher.decode(ciphertext,keystream.reset) 
puts SolitaireCipher.decode('CLEPK HHNIY CFPWH FDFEH',keystream.reset) 
puts SolitaireCipher.decode('ABVAW LWZSY OORYK DUPVH',keystream.reset) 




    - Warren Brown