Brainf*ck(o=('-'o)

こんなBrainf*ckのコードを,

+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+.

こんな殴り合い(ボクシング)に変換する.

(o'-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')≡o)゚-')=o)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')=o)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')=o)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-゚(o=('-゚(o=('-゚(o=('-゚(6('-゚(o≡('-'o)(o'-')=o)゚-')-o)゚-')=o)゚-')9)゚-')9)゚-')-o)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')-o)゚-')-o)゚-')9)゚-')9)゚-')9)゚-')-o)゚-')=o)゚-゚(6('-'o)(o'-')-o)゚-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-'o)(o'-')-o)゚-゚(o=('-'o)(o'-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')9)゚-')-o)゚-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-'o)(o'-')-o)゚-')9)゚-')9)゚-')9)゚-')-o)゚-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-'o)(o'-')-o)゚-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-゚(6('-'o)(o'-')-o)゚-')=o)゚-')9)゚-')-o)゚-'o)

誰得!

Brainf*ckの詳細についてはWikipediaを参照してね.
http://ja.wikipedia.org/wiki/Brainfuck

変換規則

変換規則は以下の通り.

変換前変換後
>(o'-')=o
<o=('-'o)
+(o'-')9
-6('-'o)
.(o'-')-o
,o-('-'o)
[(o'-')≡o
]o≡('-'o)

さらに,

  • 右向きに殴るやつが連続した場合は,殴られる側の顔を")゚"のように凹ませる
  • 左向きに殴るやつが連続した場合は,殴られる側の顔を"゚("のように凹ませる
  • 最初に左向きに殴るやつが来た場合はその左に"(o'-゚("を追加
  • 最後に右向きに殴るやつが来た場合はその右に")゚-'o)"を追加
  • 右向きに殴るやつと左向きに殴るやつが向かい合った場合は間に")゚-゚("を追加

変換コード

Ruby 1.9で動作確認済み.

#!/usr/bin/env ruby1.9
# -*- coding: utf-8 -*-

def enboxing(code)
  boxing = {
    ">" => "(o'-')=o",
    "<" => "o=('-'o)",
    "+" => "(o'-')9",
    "-" => "6('-'o)",
    "." => "(o'-')-o",
    "," => "o-('-'o)",
    "[" => "(o'-')≡o",
    "]" => "o≡('-'o)",
  }

  result = ""
  code.each_char do |c|
    next if !boxing[c]
    c = boxing[c].clone

    if c[0..2] == "(o'" && ["o", "9"].include?(result[-1])
      c[0..2] = ")゚"
    elsif result[-3..-1] == "'o)" && ["o", "6"].include?(c[0])
      result[-3..-1] = "゚("
    elsif ["o", "9"].include?(result[-1]) && ["o", "6"].include?(c[0])
      result += ")゚-゚("
    end
    result += c
  end
  if ["o", "6"].include?(result[0])
    result += "(o'-゚("
  end
  if ["o", "9"].include?(result[-1])
    result += ")゚-'o)"
  end

  result
end

if __FILE__ == $0
  puts enboxing(ARGF.read)
end

復元コード

殴り合いをBrainf*ckのコードに再変換する.殴る手の形さえ考慮すれば元のコードを復元できる.

#!/usr/bin/env ruby1.9
# -*- coding: utf-8 -*-

def deboxing(code)
  result = code.gsub(/['゚]-['゚]/, "")
  result.gsub!(/=o/, ">")
  result.gsub!(/o=/, "<")
  result.gsub!(/9/, "+")
  result.gsub!(/6/, "-")
  result.gsub!(/-o/, ".")
  result.gsub!(/o-/, ",")
  result.gsub!(/≡o/, "[")
  result.gsub!(/o≡/, "]")
  result.gsub!(/[^><+-\.,\[\]]/, "")

  result
end

if __FILE__ == $0
  puts deboxing(ARGF.read)
end

あとは復元したコードをBrainf*ckのインタプリタで実行すればOK.

殴り合いコード用インタプリタ

#!/usr/bin/env ruby1.9
# -*- coding: utf-8 -*-

class Brainfxxk
  class ProgramError < StandardError; end

  def self.run(code)
    tokens = code.chars.to_a
    jumps = analyze_jumps(tokens)
    output = ""

    mem = []
    pc = 0
    ptr = 0

    while pc < tokens.size
      mem[ptr] ||= 0

      case tokens[pc]
      when "+"
        mem[ptr] = (mem[ptr] + 1) % 256
      when "-"
        mem[ptr] = (mem[ptr] + 255) % 256
      when ">"
        ptr += 1
      when "<"
        ptr -= 1
        raise ProgramError, "ptr < 0" unless ptr >= 0
      when "."
        output += mem[ptr].chr
      when ","
        mem[ptr] = $stdin.getc.ord
      when "["
        if mem[ptr] == 0
          pc = jumps[pc]
        end
      when "]"
        if mem[ptr] != 0
          pc = jumps[pc]
        end
      end

      pc += 1
    end

    output
  end

  def self.analyze_jumps(tokens)
    jumps = {}
    starts = []

    tokens.each_with_index do |c, i|
      if c == "["
        starts.push(i)
      elsif c == "]"
        raise ProgramError, "the number of [ and ] don't match" unless !starts.empty?
        from = starts.pop
        to = i
        jumps[from] = to
        jumps[to] = from
      end
    end
    raise ProgramError, "the number of [ and ] ddon't match" unless starts.empty?

    jumps
  end
end

def deboxing(code)
  result = code.gsub(/['゚]-['゚]/, "")
  result.gsub!(/=o/, ">")
  result.gsub!(/o=/, "<")
  result.gsub!(/9/, "+")
  result.gsub!(/6/, "-")
  result.gsub!(/-o/, ".")
  result.gsub!(/o-/, ",")
  result.gsub!(/≡o/, "[")
  result.gsub!(/o≡/, "]")
  result.gsub!(/[^><+-\.,\[\]]/, "")

  result
end

if __FILE__ == $0
  code = ARGF.read
  puts Brainfxxk.run(deboxing(code))
end

ちなみにBrainf*ck用classの実装はyharaさんのRubyで作る奇妙なプログラミング言語 ~Esoteric Language~を参考にさせていただきました.