🧠

Brainf*ckするLINE BotをGASで作る

2021/09/06に公開

生きていると、無性にBrainf*ckしたくなることがありますよね。そんな時のために、LINEで手軽にBrainf*ckできるBotを作りました。

QRコード

準備

GASでLINE Botを作る手順は割愛します。以下の記事をご参照ください。

http://yoshikii.com/entry/line-bot/

最低限、開発に必要なのは、

  • ブラウザ
  • LINEアプリ・アカウント

ですが、この手の開発手段はごまんとあるので触れないでおきます。宗教戦争こわい。

処理系を書く

Javascriptでの実装例は調べればたくさん出てきます。ですが、それだと暇潰しにもならないので、オレオレ処理系を愚直に実装しました。

LINE Botにするので、逐次的な入力は求めません。Brainf*ckコードと入力を引数に、出力を返す関数を定義します。

関数 brainfuck
function brainfuck(code, input) {
  var ptr = 0;  // データポインタ
  var mmr = [0, null];  // データ配列
  var pc = 0; // プログラムの何文字目を読むかのカウンタ
  var ic = 0; // 入力の何文字目を読むかのカウンタ
  var tmp = 0;  // 二重 [ ] のために使う (後述)

  var steps = 0;
  var step_limit = 1000000;

  var output = "";

  for (pc = 0; pc < code.length; pc++) {
    switch (code[pc]) {
      case '>':
        ptr++;
        if (!mmr[ptr]) { // 配列に空要素を追加
          mmr[ptr] = 0;
          mmr.push(null);
        }
        break;
      case '<': ptr--; break;
      case '+': mmr[ptr]++; break;
      case '-': mmr[ptr]--; break;
      case '.':
        output += String.fromCharCode(mmr[ptr]);
        break;
      case ',':
        mmr[ptr] = input.charCodeAt(ic);
        ic++;
        break;
      case '[':
        if (!mmr[ptr]) {
          tmp = 1;
	  // (`]`の個数)-(`[`の個数)=1になるまでpcを右にずらす
          while (tmp) {
            pc++;
            if (code[pc] == '[') tmp++;
            if (code[pc] == ']') tmp--;
          }
        }
        break;
      case ']':
        if (mmr[ptr]) {
          tmp = 1;
	  // (`[`の個数)-(`]`の個数)=1になるまでpcを左にずらす
          while (tmp) {
            pc--;
            if (code[pc] == ']') tmp++;
            if (code[pc] == '[') tmp--;
          }
          pc--;
        }
        break;
    }
    steps++;
    if (steps >= step_limit) {
      output = "ERR: There are too many steps.";
      break;
    }
  }

  return output ? output : "ERR: There is no result.";
}

mmr[0, null] で初期化するアイデアは以下の記事を参考にしました。

https://qiita.com/masayasviel/items/0c882e88c73507c20197

出力か無いときと、処理が永遠に終わらないとき (例えば +[] ) は、LINE Botに適さないためエラー文を返します。それ以外の例外は実装していません。孟子も「人は生まれながらにして正しいBrainf*ckプログラムを書く」と言ってました。

メッセージの加工

ユーザーが送ってきた文章のうち、最初の " " で囲まれた文字列を入力とし、それ以外の文字列から ><+-.,[] を抽出してこれらを関数 brainfuck に突っ込みます。

ただ、せっかくのLINE Botなので、いくつか置換を追加しました。

関数 brain
function brain(text) {
  var code = text
    .replace(/|\!/g, '>')
    .replace(/|\?/g, '<')
    .replace(//g, '+')
    .replace(//g, '-')
    .replace(//g, '.')
    .replace(//g, ',')
    .replace(//g, '[')
    .replace(//g, ']')
    .replace(/[^><\+-\.,\[\]]/g, "");

  var input = (text + '""')
    .replace(/|/g, "\"")
    .match(/".*?"/g)[0]
    .replace(/"/g, "");

  return brainfuck(code, input);
}

!?〜ー。、() をそれぞれ ><+-.,[] に置換し、
"に置き換えます。
これで、日本語フリック入力だけでBrainf*ckできます。

LINE Botの実装

あとは、ユーザーからLINE Botへのメッセージ userMessage に対して、LINE Botからユーザーに brain(userMessage) を返信するだけです。

LINE Messaging API で返信
var ACCESS_TOKEN = '(アクセストークンをコピペ)';
var url = 'https://api.line.me/v2/bot/message/reply';

function doPost(e) {
    replyMessage(e);
    return ContentService.createTextOutput(
      JSON.stringify({ content: 'post ok' })
    ).setMimeType(ContentService.MimeType.JSON);
};

var replyMessage = function (e) {
    var userMessage = JSON.parse(e.postData.contents)
      .events[0].message.text;
    var replyToken = JSON.parse(e.postData.contents)
      .events[0].replyToken;

    UrlFetchApp.fetch(url, {
        headers: {
            'Content-Type': 'application/json; charset=UTF-8',
            Authorization: 'Bearer ' + ACCESS_TOKEN
        },
        method: 'post',
        payload: JSON.stringify({
            replyToken: replyToken,
            messages: [
                {
                    type: 'text',
                    text: brain(userMessage)
                }
            ]
        })
    });
};

最終的なコードはGistにあります。

https://gist.github.com/kagamoc/ecaa39226e8b927f88540cd6c0af658e

完成

clasp push -f && clasp deploy などあれやこれやすれば完成。

とりあえず、Hello, world!

入力を使ったこんな遊びもできます。おもしろいです。

楽しい「Brainf*ckライフ」を!!〜〜(!〜〜〜?ー)、!(?〜〜〜!ー)?。、、〜〜〜〜〜〜〜。、、?〜〜(!〜〜〜〜〜?ー)!。

参考

Discussion