# Cool fun! I found that the majority of my problems came from # misinterpreting the directions!!! That "vice-versa" thing really # threw me for a loop for a while (or was that a while-loop?). :-) # Anyway, here is my solution... #!/usr/bin/env ruby # Solitaire Cipher # Ruby-lang quiz #1 # Solution by Glenn M. Lewis - 9/27/04 $debug = nil class Card attr_reader :value, :face_value, :suit def initialize(face_value, suit) @face_value = face_value @suit = suit if suit then @value = calc_value(face_value, suit) return end case face_value when "AJoker" @value = 53 when "BJoker" @value = 53 else puts "ERROR: Unknown joker: #{joker}, should be 'AJoker' or 'BJoker'" exit end end def calc_value(face_value, suit) val = 0 case suit when "S" then val += 39 when "H" then val += 26 when "D" then val += 13 when "C" else puts "ERROR: Unknown suit: #{suit}, should be C,D,H,S" end case face_value when 2..10 then val += face_value when "A" then val += 1 when "J" then val += 11 when "Q" then val += 12 when "K" then val += 13 else puts "ERROR: Unknown card face value: #{face_value}, should be A,2-10,J,Q,K" end return val end end class Deck < Array def initialize ["C", "D", "H", "S"].each do |suit| ["A", 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K"].each do |face_value| self.push(Card.new(face_value, suit)) end end self.push(Card.new("AJoker", nil)) self.push(Card.new("BJoker", nil)) @deck_size = self.size end def dump self.each do |c| if (c.value == 53) print c.face_value else print c.value end print " " end print "\n\n" if (@deck_size != self.size) then puts "ERROR! Deck size changed to #{self.size}" exit end end def find_joker(j) self.each_index do |i| if (self[i].face_value == j) return i end end puts "ERROR: Could not find joker '#{j}' in deck." end def move_card_down(pos, num) print "before move_card_down(#{pos}, #{num}): " if $debug self.dump if $debug dest = pos + num dest -= (self.size-1) if (dest >= self.size) card = self.delete_at(pos) temp = self.dup self.clear temp.slice(0, dest).each {|x| self.push(x) } self << card temp.slice(dest..(-1)).each {|x| self.push(x) } print "after move_card_down(#{pos}, #{num}): " if $debug self.dump if $debug end def triple_cut_split(a, b) a,b=b,a if (a > b) print "before triple_cut_split(#{a}, #{b}): " if $debug self.dump if $debug temp = self.dup self.clear temp.slice((b+1)..-1).each {|x| self.push(x) } temp.slice(a..b).each {|x| self.push(x) } temp.slice(0..(a-1)).each {|x| self.push(x) } print "after triple_cut_split(#{a}, #{b}): " if $debug self.dump if $debug end def count_cut print "before count_cut: " if $debug self.dump if $debug temp = self.dup self.clear num = temp[-1].value temp.slice(num..-2).each {|x| self.push(x) } temp.slice(0..(num-1)).each {|x| self.push(x) } self.push(temp[-1]) print "after count_cut: " if $debug self.dump if $debug end def output_letter num = self[0].value card = self[num] return nil if (card.value == 53) num = (card.value > 26 ? card.value-26 : card.value) char = (num-1 + "A"[0]).chr puts "card.value=#{card.value}, char=#{char}" if $debug return char end def keystream_message(msg) # result = "DWJXHYRFDGTMSHPUURXJ" result = "" while (result.length < msg.length) do # Step 2 - Move the A Joker down one card pos = find_joker("AJoker") move_card_down(pos, 1) # Step 3 - Move the B Joker down two cards pos = find_joker("BJoker") move_card_down(pos, 2) # Step 4 - Triple cut split around two jokers apos = find_joker("AJoker") bpos = find_joker("BJoker") triple_cut_split(apos, bpos) # Step 5 - Count cut count_cut # Step 6 - Output letter - might be nil letter = output_letter result << letter if letter end return result end end message = ARGV[0].dup encrypted = true encrypted = false if (message =~ /[a-z]/) words = message.split(/\s+/) words.each do |word| encrypted = false if (word.length != 5) encrypted = false if (word =~ /[^A-Z]/) end def message2nums(msg) result = [] msg.each_byte do |c| result.push(c+1-"A"[0]) end return result end def nums2message(nums) result = "" nums.each do |val| result << (val-1+"A"[0]).chr end return result end deck = Deck.new if encrypted then puts "Encrypted message: '#{message}'" message.gsub!(/[^A-Z]/, '') # Step 1 keystream_message = deck.keystream_message(message) # puts "keystream_message = #{keystream_message}" # Step 2 num_message = message2nums(message) # puts "num_message = " # p num_message # Step 3 num_keystream = message2nums(keystream_message) # puts "num_keystream = " # p num_keystream # Step 4 num_result = [] num_message.each_index do |index| num_result[index] = num_message[index] - num_keystream[index] num_result[index] += 26 if (num_result[index] < 1) end # Step 6 result = nums2message(num_result) print "Unencrypted message: " count = 0 result.each_byte do |c| print c.chr count += 1 print " " if ((count % 5) == 0) end print "\n" else puts "Unencrypted message: '#{message}'" # Step 1 message.upcase! message.gsub!(/[^A-Z]/, '') message << "X" * ((message.length % 5)==0 ? 0 : (5-(message.length % 5))) # puts "message: #{message}" # Step 2 keystream_message = deck.keystream_message(message) # puts "keystream_message = #{keystream_message}" # Step 3 num_message = message2nums(message) # puts "num_message = " # p num_message # Step 4 num_keystream = message2nums(keystream_message) # puts "num_keystream = " # p num_keystream # Step 5 num_result = [] num_message.each_index do |index| num_result[index] = num_message[index] + num_keystream[index] num_result[index] -= 26 if (num_result[index] > 26) end # Step 6 result = nums2message(num_result) print "Encrypted message: " count = 0 result.each_byte do |c| print c.chr count += 1 print " " if ((count % 5) == 0) end print "\n" end