rustedの日記 このページをアンテナに追加 RSSフィード

ruby勉強するよ。
 | 

2006-09-19

[][]The Solitaire Cipher (#1) 22:05 The Solitaire Cipher (#1) - rustedの日記 を含むブックマーク はてなブックマーク - The Solitaire Cipher (#1) - rustedの日記

トランプを使ったSolitaire Cipherという方法を使って暗号化、復号化を行えという問題(http://www.rubyquiz.com/quiz1.html)。

この問題は手順が細かく書いてあるので特に迷うこともなくできた。

ソースはこんな感じ。

#!/usr/bin/ruby
# Ruby Quiz - The Solitaire Cipher (#1)

$letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"

# 前処理
def preprocess(s)
  s.upcase!
  s.gsub!(/[^A-Z]/, '')
  pad = (s.length % 5 != 0) ? 5 - s.length % 5 : 0
  i = 0
  while i < pad
    s.insert(-1, 'X')
    i += 1
  end
  return s.length
end

# 初期化
def initDeck(deck)
  i = 0
  while i < 54
    deck[i] = i
    i += 1
  end
end

# Joker A を下に 1 枚分動かす
def moveJokerA(deck)
  jokera = deck.index(52)
  deck.delete_at(jokera)
  jokera += 1
  if jokera >= 54
    jokera -= 53
  end
  deck.insert(jokera, 52)
end

# Joker B を下に 2 枚分動かす
def moveJokerB(deck)
  jokerb = deck.index(53)
  deck.delete_at(jokerb)
  jokerb += 2
  if jokerb >= 54
    jokerb -= 53
  end
  deck.insert(jokerb, 53)
end

# 上側の Joker より上にあるカード と 下側の Joker より下にあるカード を入れ換える
def triple_cut(deck)
  tmp = Array.new()
  while deck.first < 52
    tmp.push(deck.shift)
  end
  while deck.last < 52
    deck.insert(0, deck.pop)
  end
  while tmp.first != nil
    deck.push(tmp.shift)
  end
end

# 底のカードの値だけ一番上からカードを抜いて底のカードの上に入れる
def card_cut(deck)
  i = 0
  while i < deck.last + 1
    deck.insert(52, deck.shift)
    i += 1
  end
end

# 一番上のカードの値だけ数えたところにあるカードの値を出力する
# ジョーカーの場合はまたこんど
def getLetter(deck, key)
  i = deck.first + 1
  if i > 53
    i = 53
  end
  l = deck[i]
  if l < 52
    key.insert(-1, $letter[l].chr)
    return 1
  end
  return 0
end

# Key Stream Letter なるものを得る
def getKeyStreamLetter(n)
  key = String.new()
  deck = Array.new(54)

  initDeck(deck)

  i = 0
  while(i < n)
    moveJokerA(deck)
    moveJokerB(deck)
    triple_cut(deck)
    card_cut(deck)
    i += getLetter(deck, key)
  end
  return key
end

# 数値に変換する (A -> 1 B->2 ... ) 
def toNumbers(msg, num)
  for i in msg.split('')
    num.push($letter.index(i) + 1)
  end
end

# 文字列に変換する
def toString(num, msg)
  for i in num
    msg.insert(-1, $letter[i - 1].chr)
  end
end

# 配列と配列の足し算
def addArray(msg, key, sum)
  while msg.first != nil
    i = msg.shift + key.shift
    if i > 26
      i -= 26
    end
    sum.push(i)
  end
end

# 配列と配列の引き算
def subArray(msg, key, sum)
  while msg.first != nil
    i = msg.shift - key.shift
    if i < 1
      i += 26
    end
    sum.push(i)
  end
end

# 区切って出力
def print_out(s)
  while s.length > 0
    print s.slice!(0..4), ' '
  end
  puts
end

def encrypt(s)
  n = preprocess(s)
  key = getKeyStreamLetter(n)
  msg_ary = Array.new()
  toNumbers(s, msg_ary)
  key_ary = Array.new()
  toNumbers(key, key_ary)
  sum_ary = Array.new()
  addArray(msg_ary, key_ary, sum_ary)
  output = String.new()
  toString(sum_ary, output)
  print_out(output)
end

def decrypt(s)
  s.gsub!(/[^A-Z]/, '')
  key = getKeyStreamLetter(s.length)
  msg_ary = Array.new()
  toNumbers(s, msg_ary)
  key_ary = Array.new()
  toNumbers(key, key_ary)
  sum_ary = Array.new()
  subArray(msg_ary, key_ary, sum_ary)
  output = String.new()
  toString(sum_ary, output)
  print_out(output)
end

if $*[0] == "-d" and $*.length == 2
  decrypt($*[1].dup)
elsif $*[0] == "-e" and $*.length == 2
  encrypt($*[1].dup)
else
  puts "usage: " + $0 + " -d|-e string"
end

実行結果。

> ./quiz01.rb -d 'ABVAW LWZSY OORYK DUPVH'
WELCO METOR UBYQU IZXXX

できた!メッセージの1つは"Welcome to Ruby Quiz"。最初に解いておいたほうがよかったかなあ。

 |