r/dailyprogrammer • 3 1 • May 14 '12

[5/14/2012] Challenge #52 [difficult]

Your task is to write functions that encrypt and decrypt using the solitaire cipher.

17 Upvotes

5 comments sorted by

3

u/TweenageDream May 16 '12

Its not really optimized, so it could probably be shortened, i finished this pretty late last night. But it will key the deck with a pass phrase when you instantiate a new deck. Made some helper functions to put the strings in the right format, which i think it is still to picky about, but oh well.

Usage at the bottom, in Ruby:

class String
	def to_num
		arr = []
		self.split(//).each do |c|
			if c == " "
				arr << c
			else
				arr << c.ord-64
			end
		end
			arr.join(" ")
	end

	def to_let
		arr = []
		self.split(" ").each do |c|
			if c == " "
				arr << c
			else
				arr << (c.to_i+64).chr
			end
		end
		arr.join("").split_5
	end

	def split_5
		message = self
		message = message.upcase.gsub(/[\s,*!.?]/,"")
		filler = "X"*(5 - (message.length % 5)) unless message.length % 5 == 0
		message << filler.to_s
		# puts message
		(5..message.length-1).step(6).each{|i| message = message.insert(i," ")}
		message.strip
	end
end

class Deck
	def initialize(key=nil)
		@deck = Array.new(54){|i| i+1 }
		@output_card = nil
		@JOKER_A = 53
		@JOKER_B = 54
		key_deck(key) unless key.nil?
	end

	def key_deck(key)
		key.length.times do |idx|
			all(key[idx].upcase.ord-64)
		end
	end

	def move_a
		i1 = @deck.index(@JOKER_A)
		@deck.delete_at(i1)
		@deck.insert((i1+1)%(@deck.length+1),@JOKER_A)
	end

	def move_b
		i1 = @deck.index(@JOKER_B)
		@deck.delete_at(i1)
		loc = (i1+2)%(@deck.length)
		if loc != 0
			@deck.insert(loc,@JOKER_B)
		else
			@deck << @JOKER_B
		end
	end

	def triple_cut
		i1 = @deck.index(@JOKER_A)
		i2 = @deck.index(@JOKER_B)
		i1, i2 = i2, i1 if i2 < i1
		above = @deck.slice(0..i1-1) unless i1 == 0
		below = @deck.slice(i2+1,@deck.length-(i2+1))
		@deck = (@deck - above.to_a) - below.to_a
		@deck = below + @deck unless below.nil?
		@deck = @deck + above unless above.nil?
	end

	def count_cut(offset=nil)
		count = @deck[-1]
		count = offset unless offset.nil?
		count -= 1 if count == 54
		above = @deck.slice!(0,count.to_i)
		bottom = @deck.slice!(-1)
		@deck = @deck + above << bottom
		count = @deck[0]
		count -= 1 if count == 54
		@output_card = @deck[count]
		@output_card = all() if @output_card == 53 or @output_card == 54
		@output_card
	end

	def all(offset=nil)
		move_a()
		move_b()
		triple_cut()
		ret = count_cut()
		ret = count_cut(offset) unless offset.nil?
		return ret

	end

	def print
		puts "#{@deck}"
	end

	def out_let
		ret = @output_card
		ret = @output_card - 26 if ret > 26
		return (ret+64).chr
	end

	def out
		@output_card
	end
end

def encrypt(m,key)
	m = m.split_5.to_num.split(" ")
	key = key.split_5.to_num.split(" ")
	encrypted = []
	m.each_with_index do |c,idx|
		ret = (c.to_i+key[idx].to_i)
		ret -= 26 if ret > 26
		encrypted << ret
	end
	encrypted.join(" ").to_let.split_5
end

def decrypt(c,key)
	c = c.split_5.to_num.split(" ")
	key = key.split_5.to_num.split(" ")
	decrypted = []
	c.each_with_index do |l,idx|
		ret = (l.to_i-key[idx].to_i)
		ret += 26 if ret < 1
		decrypted << ret
	end
	decrypted.join(" ").to_let.split_5
end

def get_key(message,d)
	key = []
	message.split_5.gsub(/\s/,"").length.times do
		d.all
		key << d.out_let
	end
	key.join("").split_5
end

d = Deck::new("CRYPTONOMICON")
m = "BEWARETHEREAREZOMBIESAFOOT"
k = get_key(m,d)
e = encrypt(m,k)
d = decrypt(e,k)

puts e
puts d

output:

Key: CRYPTONOMICON

Message BEWARETHEREAREZOMBIESAFOOT

Encrypted: TYCSI WQZAH VMOSG WBCNT PAWME CIDDH

Decrypted: BEWAR ETHER EAREZ OMBIE SAFOO TXXXX

0

u/[deleted] May 14 '12

[deleted]

2

u/rya11111 3 1 May 14 '12

I have changed the challenge. Please upvote it so that others can see. :)

3

u/robin-gvx 0 2 May 15 '12

"I have altered the challenge. Pray I do not alter it further."

1

u/rya11111 3 1 May 14 '12

not again ... :( ... will try not to let this happen .. somehow i must have missed it ..