class DeCody def initialize(key); @key = key.downcase.gsub(/[^a-z]/,'') end def locate(letter); [@key.index(letter)/5, @key.index(letter)%5] end def at(row, col); @key[(row*5) + col, 1] end def encode(str); pairs, str = [], str.downcase.gsub(/[^a-z]/,'') str.scan(/..|./) do |pair| if pair.length == 1; pairs += [pair + "x"] elsif pair =~ /([a-z])\1/; pairs += ["#{$1}q", "#{$1}q"] else pairs += [pair] end end pairs.collect do |pair| l1, l2 = locate(pair[0,1]), locate(pair[1,1]) if l1.first == l2.first # on the same column l1[1], l2[1] = (l1[1] + 1) % 5, (l2[1] + 1) % 5 elsif l1.last == l2.last # on the same row l1[0], l2[0] = (l1[0] + 1) % 5, (l2[0] + 1) % 5 else; l1[1], l2[1] = l2[1], l1[1] end # normal swap [at(*l1), at(*l2)] end.join end def decode(str); pairs, str = [], str.downcase.gsub(/[^a-z]/,'') str.scan(/..|./) do |pair| if pair.length == 1; pairs += [pair + "x"] elsif pair =~ /([a-z])\1/; pairs += ["#{$1}q", "#{$1}q"] else pairs += [pair] end end pairs.collect do |pair| l1, l2 = locate(pair[0,1]), locate(pair[1,1]) if l1.first == l2.first # on the same column l1[1], l2[1] = (l1[1] - 1) % 5, (l2[1] - 1) % 5 elsif l1.last == l2.last # on the same row l1[0], l2[0] = (l1[0] - 1) % 5, (l2[0] - 1) % 5 else; l1[1], l2[1] = l2[1], l1[1] end # normal swap [at(*l1), at(*l2)] end.join end end dc = DeCody.new <<-END k x z p w i g t f a h o s e n y m b r c u l q d v END p dc.decode("le hs sq dr iw ai bs ka is ni cg ev do to")