読者です 読者をやめる 読者になる 読者になる

ターミナルから松屋がどんどん出てくる

www.adventar.org

10日目

--

12/5(土)未明

お腹が空きました。こういう夜は松屋のメニューを作りたいですよね。

というわけで、松屋のメニューを出力するgemを作成してしまいました。

github.com

既存の松屋のメニューに使われている文字列を一定のルールでつなぎ合わせます。また、このgemを入れると残念なことにmatsuyaコマンドを使えるようになり、コマンドライン上からいろんなメニューを作ることが出来ます。

どうやってランダムなメニューを作り出しているか

さて、この松屋gemで何回もメニューを作っていると、だんだんお腹が空いてきます。ランダムに生成しているので「ネギ塩豚カルビ丼」の部分文字列を取って「塩丼」みたいな食欲がなくなるような物が大量生産されそうなものですが、必ずといっていいほどごはんと呼べそうなものが出てきます。松屋gemは乱数ではなく、こころをこめてひとつひとつ手作りなのです。

メニューの製造工程

お客様に商品をお出しするまでには、以下の様な手順を踏みます。

  1. 調理フェーズ
  2. 具材合成フェーズ

調理フェーズ

実際に調理するにあたって、松屋の「プレミアム牛めし」を例にとって考えてみました。まず、プレミアム牛があって、めしがある、完成。このことから、松屋のメニューは調理方法から名前をつけていると考えることが出来ます。深夜3時だったのでこの理論を信じて疑わなかったんです。反省しています。

プレミアム旨辛ネギたま牛めしなども同じ理屈で説明がつきます。この2品に限っていえば、客に出すまでの皿の状態の遷移は次のような図に表すことができます。

f:id:toshi_a:20151207163531g:plain

プレミアムときたら、旨辛か牛めしに遷移します。どちらを選ぶかはランダムです。旨辛に遷移した場合は、ネギ、たまを通って最終的には牛めしにたどり着きます。beginから調理は始まって、endに辿り着けばお客様にお出しすることが出来ます。

この理屈で、一通りのメニューを打ち込んでみます。

f:id:toshi_a:20151209003201p:plain

工程ごとの依存関係が明確になりました。例えば「オリジナル」は、必ず最初にしか現れず、次にハンバーグ、カレギュウ、カレーに1:2:1の確率で遷移することがわかります。その先も、例えばハンバーグはカレーに遷移する可能性がありますが、カレギュウには遷移しないといったこともわかりますね。この辺りは松屋に詳しい皆さんなら納得できると思います。適当に矢印を辿って行ったらなんかそれっぽいメニューができるので遊んでみてください。

松屋gemはまさに、この矢印を辿って文字列を作るアプリケーションです。どおりで、存在するメニューや、ありそうなメニューが頻繁に生成されるわけです。書いてる時は気づかなかったのですが、最初のバージョンが出来たあと寝て起きたらこれマルコフ連鎖じゃねってなりました。実装したこと無いので違うかもしれませんが多分そうです。どんだけ眠かってん

マルコフ連鎖は数年前にTwitterでへんなBOTを作るのが流行ったので、まさに同じことをやった人は多いんじゃないでしょうか。今だったらdeep learningとかなんでしょうけどあれは知らん。

この手法を使ったBOTのなかで私が気に入っているものに、圧縮新聞というやつがあります。短いほうが面白いとか、いろいろと似通ったところがあります。

twitter.com

一応断っておくとところどころちゃんとしたマルコフ連鎖ではありません。例えばあとで言及する「具材変異」なんかがそうです。

具材合成フェーズ

これを辿って行っても作れないメニューがいくつかあります。例えば「鶏のチリソース定食」「キムカル丼」などがそれにあたります。それぞれ「鶏チリソース定食」「キムチカルビ丼」がもっとも近い答えになるでしょう。松屋初心者がよく陥る罠です。

これらを調理するには、ある隣り合った2つの素材を合成するテクニックを使用します。「鶏チリ」が「鶏のチリ」、「キムチカルビ」が「キムカル」といった感じです。

上の図を見て「+」という要素が気になった人もいると思いますが、これはミニ牛めしセットなどを表す特殊な記号です。「A + B」は「A ミニBセット」に展開されます。長いメニューを生成するテクニックとして、ネギたっぷりループ法と、セットネスト法という2つの方法が主流になりつつあります。

こういった変換を行うために今回はegisonというライブラリを使用しています。

github.com

このライブラリ面白いんですけど俺の頭ではいまいち使いドコロを見つけられない。今回の例ならString#gsubでもよかったとは思うんですが、DSLがすごくかっこよくて私は好きです。

具材変異

これだけでもまあまあ面白いメニューは出来るんですが、ビビンバに遷移したら必ず丼に遷移するなど、サンプル数が少ないため面白みに欠けます。この秩序を適度に壊すための仕組みが「具材変異」です。

調理フェーズで具材を選出するたびに具材変異の抽選が行われます。変異すると、具材の依存関係を無視して、存在する具材の中からランダムで一つ具材を選出します。更に、その次の具材は、変異後の具材の子を選出します。先のネットワーク図で言えば、ランダムな場所にワープする感じです。この仕組みによって、「牛丼」のようなメニューが出てくることがあるのです。

この具材変異の確率は、オレたちが最もリスペクトする、松屋の店員の中でもイカれた野郎と噂になっていると噂になっているおかの先生にちなんで「おかの値」と呼ばれています。あまりおかの値が高過ぎると完全なランダムと変わらなくなってくるので、標準では0.1(10%)に設定しています。

あほなものを作ったものだ。しかし…

かくして悲惨なものが出来上がったのですが、意外と毎回ありそうなメニューが出てきて、深夜だったのでセルフ飯テロになってしまいました。そこでこの苦しみをみんなに知ってもらおうと翌日にRubygemsで配布したところ、何故か一気に流行りだして、いろんな人がmatsuyaコマンドを実行しまくる大事故に発展しました。

Unix哲学を感じる。

このあとmikutterプラグインも作ったのですが、matsuyaコマンドはプラットフォームに依存せずに実行できてしまうので、私のフォロワーや、一部のやばいmikutterユーザを飛び越えていろんな人がメニューを連投するようになってしまいました。

もっと悲惨な感じにしよう

さて、これだといちいちインストールしなければいけないので流行りません。もっと敷居が下がるといいですね。

松屋ジェネレータ

というわけで、サクっとでっち上げました。JSで書こうかとも思ったのですが、こんなの二度と書きたくないというのが正直なところだったので、毎回リクエストするようにしました。

最初DBに何か記録しようとしてPadrinoで作り始めたんですが、何かって何だよって気づいた時にはだいたい完成してて、結果消防車で花の水やりやるような大掛かりな感じになってしまいました。マジでアホだ。

最近Webエンジニアじゃないから流行がわからなくてすごく古臭くなりそうだったので、HTML5などの最新の技術をふんだんに取り入れたtotori.dip.jpを参考にして作りました。

totori.dip.jp

松屋ジェネレータ画像を一枚も使ってないしサムネもへったくれもないですね。あの松屋ロゴっぽい何かはSVGですけど。

案の定多くの人がこれのせいで松屋ジェネレート沼にハマってしまって、Twitterは大変なことになりました。herokuのログがあんな速度で流れるのは初めて見たぞ

生成された謎メニューを再現するネクロマンサーも現れました。

github.com

サードパーティアプリケーションも出てきました。

本家(?)ライブラリのほうも既に執筆時点で11回のリリースを経てバージョン0.2.3がリリースされており、私の他に2人がpull-reqを送ってくださるなど、かなり活発です。

やっぱりこういうのにハマる人間はアホで、mikutterユーザもアホなので、結果としてmikutter全体の進化が数日間止まるという甚大な被害が出ました。あとソフィーのアトリエもやってません。マジでアホだ。

感想

虚無感が凄い。これが報いだというのか。どこをとってもどうでもいいことしかない。

しかし書いているときはめっちゃ楽しかったです。楽しかったなぁ…