私用領域を使って UTF-8 エンコードされた文字列にバイナリを埋め込む術

ソースコードアスキーアートや罫線文字使って図を頑張って書いて埋め込むのってめんどいことがあるよね」「どうして人類はプレーンテキストに画像も埋め込めるようにしておかなかったんだろうな」みたいなことを考えていたら、「私用領域を使って UTF-8 エンコードされた文字列にバイナリを埋め込もうとしたらどうなるかな」という発想に至った。ということで考えてみよう。

私用領域とは

https://www.unicode.org/versions/Unicode15.0.0/ch23.pdf によれば、私用領域 (private use area) とは 

Private Use Area: U+E000–U+F8FF

The primary Private Use Area consists of code points in the range U+E000 to U+F8FF, for a total of 6,400 private-use characters.

Supplementary Private Use Areas

EncodingStructure.The entire Plane 15, with the exception of the noncharacters U+FFFFE and U+FFFFF, is defined to be the Supplementary Private Use Area-A. The entire Plane 16, with the exception of the noncharacters U+10FFFE and U+10FFFF, is defined to be the Supplementary Private Use Area-B. Together these areas make an addi- tional 131,068 code points available for private use.

とのことである。つまり、基本多言語面の「U+E000 以上 U+F8FF 以下」、第15面の「U+F0000 以上 U+FFFFD 以下」、第16面の「U+100000 以上 U+10FFFD 以下」が私用領域である。

 

ちなみに、上にも書いてあるとおり、U+FFFFE、U+FFFFF、U+10FFFE、U+10FFFF は私用領域ではなくて非文字 (noncharacter) という分類になる。今回の考察では、非文字を避けることを考える。

 

余談:非文字

ところで、そもそも「なぜ FFFE か FFFF で終わるコードポイントが全て非文字なのか」という点について気になったのでちょっと調べてみたところ、なんと 2001 年 10 月 1 日(20 年以上前!)のメーリングリスト http://www.unicode.org/mail-arch/unicode-ml/y2001-m10/0014.html に書いてあった。なんでも、UnicodeISO/IEC 10646 を統合する過程で意図せず紛れ込んでしまった仕様であり、気づいた頃には既に直せなくなっていたから今も残っている、という経緯らしい*1

> > BTW, it doesn't make sense for every code position
> > ending in FFFF or FFFE to be a non character.
>
>It doesn't make much sense, but it is the rule anyway.

This crept in during the merger of ISO/IEC 10646 and Unicode, and when it was
discovered, it was too late to do anything about it.

やっていき

U+0800 以上 U+FFFF 以下のものを UTF-8エンコードすると 3 バイト消費する。基本多言語面の「U+E000 以上 U+F8FF 以下」はここに当てはまるので、6400 種 (= 12.64 bit) をエンコードするのに 24 bit が必要という計算になる。

U+10000 以上 U+10FFFF 以下のものを UTF-8エンコードすると 4 バイト消費する。第 15・16 面の「U+F0000 以上 U+FFFFD 以下」「U+100000 以上 U+10FFFD 以下」はここに当てはまるので、131068 種 (= 16.99995597 bit) をエンコードするのに 32 bit が必要という計算になる。

ということは、基本多言語面のやつのみを使うと 52.68%、追加多言語面のやつのみを使うと 53.12% の効率でエンコードできる。

「普通に 0-9, A-F の文字で 16 進数を書く」よりは若干効率よく、かつプレーンテキストと紛れることが一切ない*2となると、わりと面白い使い方ができるのかもしれない。

 

両方使った場合はどうだろうか。6400 + 131068 種 (17.0687 bit) をエンコードするのに、6400/(6400+131068)(4.6556%)の確率で 24 bit、それ以外において 32 bit を要するので、

53.96% か。じゃあまあほとんど上がらないということで。

結論

  • 最初は全く使い物にならんかと思ったが、意外とアリな気がしてきた
  • それなりには使えそうなので、いつか応用するかもしれん

*1:正直言うと、この記事で真にお伝えしたい最大の面白話は実はこっちである

*2:みなさんも英単語をファイル内検索して Base64 文字列がヒットした経験は一度や二度ではないだろう

拡散モデルに言語創作をしてほしいなぁ

この記事は創作+機械学習 Advent Calendar 2022 の 25 日目の記事です。ところで今は 2022 年 12 月 31 日です。おやぁ。

 

遅刻しておきながら、実際に計算機を回したりしているわけではない、ある意味少し的はずれな記事内容になってしまい申し訳ありません。一方で、比較的ユニークな話題をこのアドベントカレンダーに持ち込むことができるように努力したので、楽しんでいただければ幸いです。

 

なお、昨年度は 『架空世界の表意文字用の手書き文字認識を実装したい (part 1)』という記事で創作+機械学習 Advent Calendar 2021 に参加したのですが*1、今年の記事の内容はこの去年の記事と関連があるので、そちらも合わせてお読みいただけるとさらに楽しめるかもしれません。

 

hsjoihs.hatenablog.com

 

以下常体で書きます。

 

想定読者層

以下のうちのどれかに当てはまる人。

  • 人間が話す言語に興味がある人
  • 機械学習、特に拡散モデルによる絵の生成に興味がある人
  • 細かい設定を詰めた創作が好きな人

言語は一様乱数列ではない

言語は一様乱数列ではないということはマジの太古の昔から知られてきた。1843年に発表されたエドガー・アラン・ポーの小説「黄金虫」には、主人公が暗号を解読するのに、英文に登場するアルファベットは t や e がやたら多く z や x はかなり少ないという性質を用いる描写があり、この事実が人口に膾炙するきっかけの一つになった。また、言語を構成する要素の使用頻度の分布自体にも、20世紀前半の言語学者ジョージ・キングズリー・ジップが「ある言語で n 番目によく使われる単語は、一番使われる単語の n 分の 1 程度しか登場しない」という法則を発見した。

とはいえ、抽象的な話だけしていても面白くないので、読者にとって身近であると推察される日本語を例にして説明しよう*2

日本語(の標準語)*3の文の音韻的構造を記述できる非常に雑 (crude) なモデルを紹介すると、

 

const 子音候補 = ["", "k", "s", "t", "ts", "n", "h", "m", "r", "w", "g", "z", "d", "b", "p", "f"];
const 母音候補 = ["a", "i", "u", "e", "o", "ya", "yu", "yo"];
const 特殊モーラ候補 = ["", "ん", "っ", "ー"];

 

とでも定義したとき、

  • 「子音+母音+特殊モーラ」が繰り返され、「アクセント句」が作られる
  • アクセント句の中の 1 個または 0 個に「アクセント核」が付与され、「アクセント核」より後からジェットコースターのように一気に音程が下がる

としてやると、日本語の音声*4をそれなりに表せるモデルになる。

 

具体例を挙げると、「ちょっと相談がありますので」という文は

 

(画像は OJAD - オンライン日本語アクセント辞書 を用いて生成)

 

というような構造になっているので、これを

と分析してやることで、先程のフォーマットに当てはめることができる。以下、これを略記して「ちょ]っと そーだんが ありま]すので」のように表すことにする。

 

さて、このフォーマットにだけ従って、残りを一様乱数で埋めるとどうなるだろうか。日本語になるだろうか。やってみよう。

 

  • ぴょべ] でゅーせ]ー たんてぃっしょっよ]ん びゅぴ]っひょんうぃゃんばっ のっうぃゅんじゃふゅんふゃ]ー
  • きでぃゃ]ーひょ ふぃっぎゃ]っぎゃーうぉっし うぃゃーひょ] げんふぁず]ん みょ
  • ちゃーな]ー さーほ でぃゃん めーつぃゃじゃ]ってっぴーせー うぃゃちょーめ]ーぬっ

知 っ て た。

 

RGB にランダムな値をセットしたものが絵と呼びうる代物では到底無いのと全く同じように、フォーマットにだけ従って音素をランダムに並べたものは到底人間の話しうる言語ではありえない。

「RGB にランダムな値をセットしたやつ全体の集合」の中の非常に小さい部分集合のみが絵と呼びうるのと同様に、「フォーマットにだけ従って音素をランダムに並べたもの全体の集合」の中の非常に小さい部分集合のみが、人間の話しうる言語として plausible でありうる。

人の話す言語が好きである。人類は言語についてまだ全然なにも分かっていないにもかかわらず、とりあえず意味を伝えることができている。如何なるプロトコルで伝達すべきかというマニュアルがRFCを読みに行けば書いてあるコンピュータの分かりやすい世界と異なり、どこにも網羅的な仕様書が書かれていないのになぜか赤ん坊は言語を学ぶことができるし、大人も十分頑張れば結構上手に言語を学ぶことができる。非常に面白い。コンピュータの言語では0x00から0xFFまで全てを合法として意味を与えることは普通であるが、人間の話す言語はスカスカであり、「ムスビメ」には意味があるが「ケセリマ」には意味が無い。このスカスカさは聞き間違いを減らす役割を果たしていると言われていて、人間が言語を話す際の情報伝達速度はどの言語でも秒速39ビットであると主張する研究もある。面白い。さらに、言語は時間がたつにつれ変化し、ある音が別の音と区別されなくなったりする。しかし、そういう「減る作用」に対して、複合語などの形成による「増える作用」が上手くバランスを取っているのが本当に面白い。
私の過去記事『セキュリティ・ネクストキャンプ 2021 応募課題晒し』より引用

 

さて、ここまでお膳立てをすると、私がなんの話をしようとしているのか察することができる方も多いであろう。最近世間をとてつもなく賑わせて久しい、拡散モデルによる絵の生成である。

 

 

拡散モデル

「言語に詳しいが、機械学習とかにあんま興味を持ってこなかった」という人もこの記事の想定読者に位置づけているので、雑に拡散モデルというものの紹介をしようと思ったが、書くのが面倒になってきたので、人様の記事を丸々引っ張ってきて紹介とする

さて、ここからはAIの仕組みの話になります。「AIは無意味なノイズからノイズを取り除くことでイラストを生成している」と聞いて、少し混乱するかもしれません。ノイズからノイズを取り除いたら、何も残らないと思いますよね?大丈夫です、ちゃんと説明します。

 

Stable DiffusionNovelAI Diffusionといった最近の画像生成AIは、「拡散モデル」という仕組みを使っています。拡散モデルは、次のような仕組みです。

  1. 画像と、それにノイズを加えたものを用意する
  2. ノイズを加えた画像から(ノイズを取り除いて)元画像を推測できるようにAIを訓練する
  3. 無意味なノイズを与えると、存在しない「元画像」を生成できるようになる

これを人間で例えると、こんなイメージです。

※厳密には、AIの場合はノイズあり画像と元画像の両方が与えられます。

  1. ある国ではイラストが禁止されることになり、モザイクのかかったイラストしか見られなくなった
  2. 人々は、モザイクのかかったイラストからもとのイラストを想像するようになった
  3. 人々に「モザイクのかかったイラストのように見えるが実際は無意味な画像」を見せると、その画像からイラストを想像しようとする

これが、拡散モデルの仕組みです。

Robot-Inventor『イラスト生成AIに対するよくある誤解』より引用

同様のことが言語に関してもできてくれないかな〜、というのが、この記事で私が一番言いたいことである。

え、文章生成 AI はもうあるよね?

もちろん。私が求めているのはそれではなく*5

 

「語彙・文法現象・例文・その言語ならではの特性を生かした文学」とかを内部一貫性を保った形で持った、「いかにも人間が話しそうな」言語が創作されること

 

である。

これに関して、ともに言語創作をしてきた Μίττον(みっとん)に感想を求めてみたところ、以下のような返事が得られた。

文章生成 AI って言語構造にただのりしているからなぁ。我々は言語という制度の体系を作りたいのであって、文章生成 AI が書いてくる日本語や英語の文章という『既存制度にうまく適応した例』でしかないものは、おもろいが、おもろいだけという気持ちになる。多分この世には『お絵かきAI、美術史上の動向に乗っかることはできるだろうけど、今のところ動向を作れそうにはないよね』とか言っとるやついるだろ。

 

どういうこと?

さっき貼った「一様乱数列な日本語」を録音してくださった id:yuchiki1000yen が非常に上手く記事にまとめてくださっているので、まずそれを引用すると、

創作自然言語というものを知っているだろうか? 我々が普段喋っているような言葉を、プログラミング言語や数式体系などと対比させて「自然言語」と呼ぶことがある。 その「自然言語」のようなものを、あたかも劇を作ったり、歌を作ったり、小説を書いたり、ゲームを作ったりするのと同様に、創作する人々がいる。私は、この「人によって創作された自然言語」を「創作自然言語」と呼んでいる。 一般にはその界隈の人には「人工言語」と呼ばれることが多いがプログラミング言語や何らかの符号体系も「人工言語」と呼ばれて名称が被るので、私は好んで「創作自然言語」と呼ぶことが多い。 自然言語創作と言ってもピンと来ないかもしれないが、例えば

  • この言語にはこういう語彙があり
  • この言語はこういう文法を持ち
  • この言語の動詞はこういう活用をして
  • ...

のようなものを決めて行く営みだと思えばよい。 創作物の言語がなんであるかのスタンスは創作者によってまちまちであり、例えば「ゲームや小説の中のように架空の世界に架空の民族がいて、彼らが喋っている言語」というスタンスで行う人もいれば、「この言語は我々現世の人が趣味として/何らかの目的で用いて意思疎通するためのものである」というスタンスを取るものもある。 全者の例だと「星界の紋章」という小説で、その中の民族が用いているという設定で作られたアーヴ語というものがあり、後者の例だとエスペラント語というものがある。

今回は前者のスタイルの創作自然言語のひとつである、バート語というものを勉強してみた。

yuchiki1000yen『創作自然言語バート語の文法を勉強した』より引用

 

ということで、私は、この記事で登場するバート語という言語を(他人から引き継いで)創作している。

 

他にも、その言語が話されている(という設定の)文化圏で広く遊ばれている(という設定の)ボードゲームのオンライン対戦を実装したりなどしているが、それは今回の記事にはそんなに関係ないので、過去記事を参照されたい。

 

さて、先程「内部一貫性」という単語に言及した。創作をする上でそれなりにこだわっているポイントなので、もう少し詳しく具体的に述べていきたいと思う。

 

たとえば、知り合いのこういうことをツイートしているような人に、

 

「今年中に記事を書きたいので今から2時間半以内に語源を遡ってこうこうみたいな話をしたためてください」という無茶ぶりを頼んでみたところ、

こんにちは,Мороже です。僕はポケモンというゲームを少し触れています。ところでポケモンというゲームは日本で購入してもセーブデータを作成する前に言語を選択することができ,勉強になるかなぁという思いでORAS以降のポケモンを全てドイツ語でプレイしています。
さて,シャンデラという名前の通りシャンデリアを意識したようなデザインのポケモンがいるのですが,これのドイツ語名は Skelabra です。シャンデラはゴースト・ほのおタイプのポケモンであることを踏まえると,Skelabra はドイツ語 Skelett「スケルトン」とラテン語 candēlābra(pl.) < 単数形 candēlābrum「枝付き燭台」をミックスしたものだと思われます。みなさんご存知の天井から吊り下げる「シャンデリア」はこのラテン語 candēlābrum /カンデーラーブルム/ からのフランス語 chandelier /シャンデリエ/,からの英語 chandelier /シャンデリア/ に由来するカタカナ語ですね。つまりシャンデラと Skelabra はその見た目に反して同語源の単語をそれぞれネーミングに含んでいることが分かるわけです。
カンデーラーブルムがシャンデリエになった経緯の背景として,ラテン語の /ka/ の音が古フランス語(9 世紀ごろ)で /t͡ʃa/ (チャ)になり,数世紀かけて /ʃa/ になったことが知られています。音変化というのはある程度規則的に起こるものであり,もっと別の例を挙げることもできます。
・「シャルル」というフランスの人名がありまが,これは Charles と書き,ラテン語 Carolus /カロルス/ に由来します。Charles は同じスペルのままで英語でチャールズと読めますが,これは英語くんがフランス語側で ch の発音が /t͡ʃ/ だった時代にこの語を借用してきたことの名残ですね。
・「オーシャンゼリゼ」の歌で知られるシャンゼリゼは les Champs-Élysées と書きます。Élysées の部分は古典ギリシャ語 Ἠλύσιον エーリュシオン「ギリシャ神話の死後の楽園」で,champ は「野原」です。元となったラテン語は campus /カンプス/ で,これはコクヨの Campus ノートや,大学構内を表す英単語 campus およびそれ由来のカタカナ語「キャンパス」でお馴染みですね。野営地を表す英語 camp もラテン語 campus に由来します。
シャンデラに少し話を戻すと,ラテン語 candēlābrum 「枝付き燭台」は candēla 「蝋燭」から来ていて,英語 candle「蝋燭」はここに由来しています。光度のSI基本単位であるカンデラ(記号 cd)もこれですね。
ついでなのでヨーロッパから離れて東南アジアの言語の話もしましょう。タイ語では,月曜日のことを วันจันทร์ /ワンチャン/ と呼び,วัน /ワン/ が日,จันทร์ /チャン/ が月のことです。ところでタイ文字をやったことがある人は分かるかもしれませんが,จันทร์ という綴りにはなんと /t͡ʃandr/ と書いてあり,最後の dr という部分にはガーラン (การันต์) という黙字記号(←語源を表しつつも発音を表すための優良システムです。英語とかも採用してくれないかな〜)が付いています。というわけでタイ語 การันต์ /チャン/ はサンスクリット語 चन्द्र /チャンドラ/ 「月」に由来するわけなんですが,これは原義を辿ると「光るもの」であり,ここでさっきのラテン語の蝋燭 candēla(< candeō「光る」の名詞形)との繋がりが見えてきますね。先ほどフランス語の話で述べた /k/ > /t͡ʃ/ の変化がサンスクリット語でも起きていることも確認できます。サンスクリット語ラテン語が共通の祖先「印欧祖語」を持ち,さらにタイが仏教圏であるためにサンスクリット由来の借用語を大量に持つおかげで,タイ語の「月曜日」とポケモンシャンデラが回り回って繋がるの,とても面白くないですか?
(以上: https://nitrone7.hatenablog.com/entry/2022/12/15/210832 に書いた文からいろいろ加筆)
ところでこの文章は「今年中に記事を書きたいので今から2時間半以内に語源を遡ってこうこうみたいな話をしたためてください」と hsjoihs さんにいきなり言われて急ぎで生成したものです。無茶振りか???

 

という記事を書いてきてくれた。すごい。マジでありがとう。

 

こういう風に、「語源を追いかけていくことで、数多くの言語が回りまわって繋がっていく」という話を何時間でも続けられるような我々が、こういう部分にまでこだわって言語を創作すると、わりと同様の解像度で我々の創作した設定について語れるようになるのである。

我々の創作している「東島通商語」という言語で書かれた、架空世界の伝統的ボードゲームのルールブックを、日本語及び別の創作自然言語であるリパライン語で文法解説している文書。 全文は この Google Docs に置いてある。

こういうオタク語りが無限にでき、しかもそれが実在の言語についてできるオタク語りと同様の質であってほしい*6、というのが、私が述べた「内部一貫性」の意味である。

我々はどのようにして言語を作っているのか?

「言語を作っている」という話をすると、「言語なんていったいどうやったら作れるんですか?」と訊かれたりする。絵を描いている人や曲を作っている人に対してはあんまりこういう質問が来ているのを聞いたことがない*7が、私にはしょっちゅうこういう質問が来る。

 

ただまあ、人間がどうやって絵や曲を生み出すかを考えれば、答えはおのずから明らかであって、

「あたらしい呪文はどうやってできるんですか? 本を読めば、これこれをするための呪文を発明した人、というのはいくらでもでてきますが、どうやって発明したかはどこにも書かれていません。」

男はローブのなかで肩をすくめる。 「それをいうなら、あたらしい本はどうやってできる? 本を多く読む人たちの一部は、いずれ本を書くこともできるようになる。 どうやってか。 それはだれも知らない。」 

「本の書きかたを説明している本ならありますが——」

 「そういった本を読んでも有名な劇作家にはなれない。 その手の助言に一定の効果はあるとして、説明しきれない謎はのこる。 呪文が発明される仕組みにもおなじようなことが言えるが、こちらはさらに純粋な謎だ。」

「ハリー・ポッターと合理主義の方法」90章「それぞれの役割(その1)」より引用

要するに、雑に要約すれば「たくさん入力を入れ、なんか手を動かすと、それなりにいい感じの出力が得られたりする」といういつもの話になるのである。

ということで、我々が言語を作るようなプロセスは、具体的には以下のとおり。

1. 手動で語学書とか言語学論文とかを読み漁って「言語ってこういう挙動をするのか〜」というデータセットを脳に蓄える。

過去記事を引用すると、

文法書の全ページを眺める作業(この行為に「読む」という動詞を用いていないことに注意。つまり眺めただけで読了後になんも覚えていない様を表す。)

語学と私』より引用

さらに、わたしは「人間の言語ってどういう挙動をするんだろう?」という問いに対するデータセットをなるべく脳内に蓄えたいというモチベがあるので、語学書とかを買いまくって乱読したりしている

語学と私 (part 4) ― ご縁あってナワトル語の履修が 3 年目に入りました』より引用

 

こういう作業をし、いろんな言語を見て、「言語ってこういうことをするんだな」というデータセットが脳内に構築されると、それをもとに新たな言語が作れるようになったりするのである。

 

ただ、機械学習の界隈においても「画像生成 AI が白人男性の顔ばかりを生成してくる」という話が問題になったのと全く同様に、日本において知名度のある言語の多くが西欧的言語であるというサンプリングバイアスにも気をつけねばならない。

 

つまり、「西欧の付近では共有されているがそれ以外の地域では全然発生しない、言語全体を通して見たときには稀な現象」*8 を強く拾いすぎてしまうと私の目的に合わないので、そういうのに気をつけるために、私に直接的な経済的利益をあんまりもたらさないような言語を幅広く学び、質の良いデータセットを作るというモチベーションが発生する。

 

本棚写真

本棚写真その 2(NHK 出版のこのシリーズ、あとは中国語だけなんだよな)

本棚写真その 3

本棚写真その 4

2. 脳に蓄えた天然学習済みモデルからゆっくりと滴り落ちてくるものを全部かき集めて陳列する

大量のインプットを入れると、なんかいい感じに少しずつアウトプットが得られるので、それをかき集めて陳列していくことで、創作が進んでいく。私の場合は、ここに陳列してある。

jurliyuuri.github.io

試しにブラウザの印刷ダイアログで印刷してみようとしたら、122 枚あった。びっくり。そんなに書いたのか。

3. 人がそれを読んでなんか理解が生え、創作した言語が使用されていく。

世の中には、語学書をたくさん読んできたがゆえに、文法と語彙とそれらに関する考察が陳列されていると、そこから言語を学んで文章やら詩やらをサクサク書けるタイプの人類が存在する。なんなら私もそういうタイプの人類である。

 

ゆえに、そういう人の前にこういう情報を見せると、なんなら 6 ~ 7 時間ぐらいの学習時間で理解が生え、創作自然言語で書かれた既存文章を読解できるようになったりするのである。

yuchiki1000yen.hatenablog.com

yuchiki1000yen.hatenablog.com

 

これまた Μίττον(みっとん)に訊いてみたところ、

この感想は、私がラテン語だのサンスクリットだの割と語形変化の複雑な言語をまあまあ不親切な教科書で習得するのを強いられるのが割と好きで、かつ数学科とかいう、ある意味で一生語学をしているような専門だから出てくるものなのかもしれませんけど、人の陳列棚を眺めていると、そこから背後にある言語を割と復元できますよね。

と言われた。

 

私としても、私の創作した言語でみっとんが普通にいい感じの詩とかを書いてきたことにびっくりし、リパライン語でこのような詩を書いたりした*9

mi krante als'd als mal alfal kinunsares.

私はなんであっても全て書いていて、大抵公開している。

 

akrantier mi le tesyl lot krante lkurftless.

(それを)読んだ者は、私より上手に言語を書いた。

「set.2021:10:29 nancanhiafopyek」より引用

 

4. 言語を運用して集積していると、創作者本人も気づいていなかった言語現象がどんどん観察されていき、記述が増えていく

我々が普段話す日本語の細かい文法についてすらすらと精細に記述できるわけではないのと同様に、

一旦言語を作り始めて運用してしまったが最後、別に文法書に書いて明示的に設定したわけでもないのに「なんかこれはこうやって言うのが自然な気がする!」「たしかに文法書を読むとそういう言い方もできそうに読めてしまうけど、それは超絶不自然。そんな文構造は無理!」といった直観が勝手にニョキニョキ生えてくる。

この中の skurlavenija.mavija がみっとん、jekto.vatimeliju が私である。

こうなってくると、「言語を作っている」というよりも、母語話者に『この文って言える?言えない?こういうこと言いたいときってどうやって表現するのが一番自然?』という調査を行ってそれを記述していく、言語学者の中でもフィールドワーカーと呼ばれる職種を体験しているような気持ちになる。

 

(本職のフィールドワーカーとはどのようなことをする人か、に興味がある方は、古本 真『第1回 フィールドワーカーの苦悩:フィールド言語学とは何か』などをご参照あれ)

 

バート語の創作はWebページ『バート語について考察する』に記録されている。この記録から推察されるように、最近のバート語創作は jekto.vatimeliju による内省によって多くが進められている。その結果として、動詞変化が細かく分類すると20以上に分かれることが判明するなど、現代言語学のメスが入り、思いのほか複雑な現象を起こしていることが明らかになってきた言語の様相を呈している。

ここでは(jekto.vatimeliju による内省によって記述された)音声的な現象を一つ取り上げる。バート語には、特定の条件を満たした語中音節は高く読まれるという現象が存在するが、合成によってその条件を満たすようになった名詞句中の音節は高く読まれないという例外が発見された。例えば /ɣaː.ʋa.nuː/ の /ʋa/ は高く読まれないが、それに /niː.maʃ/ を合成した /ɣaː.ʋa.nuː.niː.maʃ/ の /ʋa/ は高く読まれるための条件を満たす。しかし、上の例外から実際にはこの /ʋa/ が高く読まれることはない。

みっとんが書いた大学レポート(非公開)より引用

我々はなぜ言語を作っているのか?

これもまあ、絵を描いたり作曲をしたりする人たちがそれらの行為をするのと同様の理由であり、私の場合は以下の通りである。

 神の作り出した美しい自然の景観を見て、風景画を描かずにはいられない人というのがいる。風景画をコンピュータに描かせずにはいられない人というのがいる。もちろん、実際の風景というのは、「太陽の中で核融合によって生成されたガンマ線が長い時間を掛けて可視光として放出され、それが膨大な距離を経て地球に届き、クロロフィルの吸収特性により緑が他の色ほど吸収されず、主に緑が多めの反射光が網膜に届き、錐体の吸収特性にしたがって脳に電気信号として入る」といった、気の遠くなるような計算量を世界がやってくれたことに対する帰結である。しかしながら、それよりも遥かに慎ましい計算量によって、風景画を描いたり、風景画をコンピュータに描かせたりできる。


 ということで、山川を見て山川の絵を描くのを好む人がいるように、私は人間の話す言語を眺めるのが好きだし、人間の話す言語を創作し描くのも好きである。

セキュリティ・ネクストキャンプ 2021 応募課題晒し』より引用

とはいえ、今まで長々と語ってきた内容から容易に推察されるように、言語を作るのは普通にめちゃめちゃめんどくさい。ゆえに、ゆったりまったり作っているといつまで経ってもあんまり進まない。もちろん着実に育っていってはいるのだが。

 

ところで、最近のお絵かき AI の台頭により、私の言語・言語創作アカウントの方の TL でも「AI が言語創作をするようになってきたら」みたいな意見を見かけた。

 

まあこういう意見に私が全く納得しないかというとそういうわけではないが、一方で 6 年間もうだうだやっていると「途中まで言語作ったから、あとはその間を納得行く感じに埋めておいていってくれないかな」という気持ちがだんだん強まってくるのである。

 

 

拡散モデルが言語創作をしたら

拡散モデルで言語創作が行われるなら、それはどのような作られ方となるのであろうか?

 

今までの絵が、「人がストロークを一つ一つ書いていき、それを重ね合わせることで作られていた」のに対して、拡散モデルによる絵というのは「絵とは呼べないノイズから、ノイズでない部分だけを残す」という作り方によってできている。なので、これの創作自然言語版ということは、「長大な一様乱数列をまず用意し、そのうち『言語と呼べない部分』を削り、言語な部分を残す」という作り方になることだろう。

 

さて、手元の「現代ヘブライ語辞典」は 30 行 2 段組、1 行 18 字の日本語で 488.5 ページあり、「約 17,000 語を収録した」と書いてあるので、30 * 2 * 18 * 488.5 = 527580 文字の和文があるということになる。和文で使う文字なんて JIS 第一水準ぐらいあればだいたいなんとかなるだろうし、1 文字辺り 12 bit ぐらい割いてやればよかろう。ということは…

え、そんなもんか。800 キロバイトの一様乱数列とか普通に既存手法の拡散モデルが扱ってるようなサイズなのでは。それを上手いこと「辞書」へと変えていけば約 17,000 語を収録した辞書になる、と考えると、私が長々と語ってきたこの妄想も実現そんなに遠くなさそうかもしれん。

 

ところで私が造語をするときの方法の一つに、「非一様乱数列を引いてみて、『この語形はこの言語に合わない!』と直観的に判断したものを除き、『まあこの語形ならいいんじゃない?』となったものを採用する」という過程がある。

 

 

zero[21/154]→e[236/308]→n[104/353]→í[258/308]→h[144/353]→a[30/308]→m[32/308]。ふむ、eníhamúḷか。うーん、なんだろう。

  • ena.ar「言う」cf. xauternaz
  • -íh- 謎
  • amúḷ「勝る」

いや、こんなferet nos extra → fenestraみたいな怪しい説立てるよりは、「語源不明」にしておくべきだろう。

どうしてもしっくりこないので振り直し。s[77/154]→e[223/308]→h[145/353]→á[155/308]→z[163/353]。seházúḷか。あーこれいいな。dházúḷ「楽しい」とも似ているし。

バート語の造語をするpart2 30. 「嬉しい」「喜ぶ」』 より引用

 

これをやることで plausible なものが作れているという実体験から、「乱数列のうち、気に入らない部分を捨てて言語とする」を全てにおいて適用してドカッと言語を作るのも、わりといけるんじゃないか、と妄想している。

 

これに対しての Μίττον(みっとん)の感想は以下の通り。

なんなら語形に対する直観もなぜかある程度伝播するからな。たとえば、前に私が akuti という単語の同根語をバート語に作ろうとしたとき、一瞬 akat という語形を提案したいと思ったが、「バート語はこれほどに短い単語を嫌う」という直観が働き、ághat という語形を提案することができた。こういう直観が学習できるの、非直観的。

なんか『かなり初級の学習者でも、【この単語はこの言語にはないだろ】というのは直観的に判断できるらしい』という論文を昔見た覚えがあるが、出典失念。

絵と言語創作は何が違うか

絵や小説といったものは大量にインターネットに放流されており、また絵ならピクセルの並び、小説ならコードポイントの並びという、その最もプリミティブな形に関しては非常に機械に扱いやすい形としてインターネット上に放流されているし、それを生産する人も消費する人もおびただしい。一方で、私の脳内のデータセットの元になっているものというのは、

  • paywall の向こう側にあったり一般公開されていたりする大量の書籍や論文
  • 人に語学を教えることが目的であって、別に言語の包括的な記述を試みているわけではない雑多な情報源
  • 同様のデータセットを構築することに興味がある同好の士(言語創作者に限らず、言語類型論という分野の研究者も比較的近いところにモチベがある)がまとめてくださった様々な情報
  • 実際に私が自分で既存の言語の統計を取って分析して curve-fit したもの
  • 似た趣味のやつらと何十時間何百時間何千時間と Discord 通話して得られた様々な断片的情報

といったごった煮であり、このごった煮にはまとまりもなければ、「私もやってみたい!」という人々の興味もあんまり引けないような代物である。

 

また、絵は、生成しておけば人間側が勝手にいい感じに解釈してくれる(うれしい)。一方で、言語は、その言語での文だけ生成しても面白くなくて、そこに既知言語での解釈がついてこそ、そしてその解釈が文法構造を成していて内部的一貫性があってこそ、その言語は言いたいことを何でも言える無限(文字通り無限)の可能性を秘めた存在となるのである。

 

私にとってお前の創作は自明に大木だ。

mi io zi'd likka jexi'ert es cyfoijt. 

 

それを使って私たちはなんでも書ける。

cene la lex leus miss krante als faller als.

 

奇妙な小説も大木に訳すことができる。

xorln nyeyl'i at elx cene akrucyfoijt.

 

その能力に私は感動したのであった。

anfi'estana'c fheocaf mi's edixe.

私が創作自然言語リパライン語で書いた詩である「set.2021:9:11 cyfoijt」より抜粋

 

なお、余談だが、「その言語での文だけ生成しても面白くな」いを如実に表す例として、私は「特に意味のない韻律詩書くか〜」とある日思い立ち、韻律詩を無から数千行吐き出している。

 

また、既知言語であっても、意味が無だとこれまたあんまりおもしろくないということを示す例として、昨年「一億総教祖時代を強力にサポートするための、お手軽聖典作成技術」( https://zenn.dev/hsjoihs/articles/2dd0287e32a982 ) と称して、「言語と呼べないような乱数列を機械翻訳にぶち込むことで、言語としての体裁だけは整っている代物を吐き出す」という実験を記事にした。

終章 男 vs. 警備員

流石に人の痕跡があるもので、休憩から同じ長い時間のプログラマーと家系の影に言及しながら食べるために、両方のローンを取る貸主の後に大きな手放すものですが、いくつかは、久保で合計を与える。作った。自衛官って、自分から数人のパーティーみたいなものだよね。最初は真ん中の方にあります。2つは、ヨーロッパの細かいシャットの人々の2つの裁判官は、理解を放棄し、彼らは販売時にチューニングされています。建てられたラインから、コミュニティに群がる小さな雷鳴が疑惑の日が続く中でチャンスを叱り、それを混ぜ合わせた。

ということで、私は拡散モデルが流行る前から「一様乱数列を加工して、それを『人間の話しうる言語として plausible である代物』という小さなサブセットに落とし込めたらうれしいのになぁ」と妄想し続けていたので、その妄想が(絵という、私が全然慣れ親しんでいない分野ではあるが)大いに流行り世間の話題を動かしているのはおもろいなぁと、勝手に思っているのである。

*1:xbar_usui 賞をいただけました。ありがとうございます。

*2:機械翻訳でこの記事を読んでいる非日本語話者がいたらすまない。

*3:本当は「標準語」というより「共通語」と言いたいのだが、この記事の想定読者には「標準語」と言わないと通じない可能性があるので、分かりやすさを優先する。

*4:実は「音韻」

*5:私も創作をする上で現状の文章生成 AI には既にだいぶお世話になっていますけど

*6:要するに、全ての単語が全ての単語と語源的つながりがある、というのもまた現実的ではないよね、というだけの話

*7:「どういうツールを使って曲を作ってるんですか?」といった質問は頻繁に発生しているのだろうが、「曲を作るなんて……いったいどうやって!?」という点を疑問に思って問う人は見たことがない

*8:ここらへんの話はStandard Average European で調べるともっと詳しく出てくる

*9:このリパライン語というのも創作自然言語である。

語学と私 (part 4) ― ご縁あってナワトル語の履修が 3 年目に入りました

この記事は 言語学な人々 Advent Calendar 2022 の 25 日目の記事です。

 

今までのあらすじ

hsjoihs.hatenablog.com

 

hsjoihs.hatenablog.com

 

hsjoihs.hatenablog.com

 

最後にこのテーマについて記事を書いたのが 1 年半前という遅筆。今日も「アドベントカレンダーに登録したからには記事を書かなきゃなぁ」と思いつつ、昨日のワクチン接種の副反応を言い訳にスーパーマリオ 64 の A ボタン縛り TAS の通史動画を見るなどの有意義なクリスマスの過ごし方をしていたところ、

 

このアドカレの主催者である id:yearman(まつーらとしお)さんから催促ツイートが来たので、いま大急ぎで文をしたためているというわけです。

 

さて、そんな話はどうでもよくて、本題に移りましょう。

本題

詳しくは過去記事を見ていただければと思いますが、授業一覧を眺めていたところ、「今まで私の人生になんだかんだ少しだけ縁があった言語であるナワトル語の授業が開講されている」ということに気づき、履修してみることにしました。

このように、1 年目は「Zoom に TA がいて、ネイティブの先生がスペイン語で話した内容を英語に同時通訳して Zoom チャットにひたすら書き込む」という方針だったのですが、2 年目になったらその TA がいなくなり、スペイン語を真面目に聞き取る必要が発生しました。

 

普通にスペイン語そんなにわかんないので大変で、授業も正直そんなに理解できていない気がします。その中でも特にキツかったのはこれと、

 

あとこれでしたね。

 

さて、なんとか 2 年目も終わらせ、夏休み。アメリカの大学は秋学期から年度がスタートするので、次の年度の履修を確認してみたところ、SPECLANG 103A: Third- year Nahuatl- First Quarter は Last offered: Autumn 2017 と書いてあり「あー、今年は 3 年目のナワトル語の授業は開講されないのか。まあしょうがないよね。じゃあ ASL (アメリカ手話) でも取ろうかなぁ」と思っていたところ、

 

つまり「そっちの都合に合わせて開講するからどのタイミングなら空いてるか教えてくれ」というメールが来ました。ということで、3 年目も履修することとなったのです。

今学期の内容

 

ちなみに、前述の通りわたしはあんまりスペイン語ができないので、

  1. 渡されたナワトル語文を急いでなんとかスペイン語に逐語訳する
  2. その逐語訳に(主にスペイン語文法上の)誤りがあるので、それをネイティブの先生に直してもらう
  3. もう一度直された文を読み上げ、これで本当にいいか確認する(一般に、文字だと気づかない誤字も、耳で聞くと気づくことがあるので)
  4. その文を DeepL に突っ込み英文にし、「あ〜〜そういうストーリーラインなのね〜」と納得する

というプロセスでなんとか授業に喰らいついていっています。なので上記のわたしの解釈には普通に誤読があるかもしれません。

 

あんまり言語学の話ができていないな

そういえばこれは「言語学な人々 Advent Calendar 2022」の記事なのでした。このままでは語学記事になってしまう。(まあ「言語にまつわるエピソード,エッセイ」もオーケーとのことですが)

 

ということで、もうちょい言語学寄りっぽい話でもしますかね。日付が変わるまでにあんまり時間がないので、とりあえず音声の話だけ。

帯気

スペイン語と同様、オンセットの破裂音は帯気しません。

2022 年 12 月頭に「みなさん IPA で書かれた音素列を見てどれくらいいい感じに初見の言語読めるんだろう」と気になって、

teˈnant͡siˈtˡawelkʷalanˈtojakemamoˈkʷapʰkipampaʔaʃˈwel̥kikiwaˈlikaˈʔatˡʰpanˈʔajatˡʰ wannel̥kʷaˈlaŋkikemakiˈʔitakʰˈnopatˡaˈjolitˡeŋkitepehˈtehki | t͡ʃikomeˈʃot͡ʃitˡʰkitentoˈjajapaŋˈkomitˡʰ

を読み上げさせてみたところ、ゆーちきさん (id:yuchiki1000yen) がやってみてくださり、

 

「ガチ初見がこんな感じ」として以下の音声を、

https://cdn.discordapp.com/attachments/831728193031503933/1049305766831595531/334e4a9858e9a718.m4a

 

「数分練習したあと」として以下の音声を提示していただけました。

https://cdn.discordapp.com/attachments/831728193031503933/1049305870766448711/e0c38957c8d7515b.m4a

 

感想としては、全体的にかなり上手く読みあげられているものの、「この言語、オンセットは帯気しちゃいけないんですよね」という気持ちが第一に出てきました。

 

日本語はかなり有気と無気の両方が free variation で頻出する一方で、ロマンス諸語とかは「有気と無気に対立がないといえばないが、一方で有気で読まれるとそもそもめちゃめちゃ違和感がある」んだなぁというのを改めて実感しました。

 

ちなみに、https://www.sil.org/system/files/reapdata/47/26/65/47266563262242678149396571357053294485/15362.pdf に明示的に記載がある通り、コーダは帯気します。韓国語みたいな

  • 입다「着る」 [ip̚t͈a̠]
  • 있다「いる・ある」[it̚t͈a̠]
  • 읽다「読む」[ik̚t͈a̠]

を聞き分ける苦労とは無縁。

声門閉鎖

スペイン語は母音連続の間に意地でも声門閉鎖を入れず、同じ母音が連続するときにはなめらかに繋げて読まなければなりません。

しかし、ワステカナワトル語では二つの母音が形態素をまたいで連続するときには間に声門閉鎖が挟まれるという現象があり(先述の https://www.sil.org/system/files/reapdata/47/26/65/47266563262242678149396571357053294485/15362.pdf にもそう書いてある)、その影響なのか、スペイン語で va a comprar とかを言う際にも先生は a の前に声門閉鎖を入れていました。

o と u

ナワトル語*1は o と u を区別しません。それが如実に現れていたのが、1 年目の授業で先生が未来形の解説をしているときのこの一枚。

スペイン語では未来を futuro というのですが、ご覧の通り、右では futuro と綴っているにも関わらず、左では foturo となっています。なるほどなぁ。

 

感想

ナワトル語学習歴ももう 3 年目に入りましたが、なんだかんだ面白くて貴重な経験ができていると思います。

 

まず、意外とインターネットにはナワトル語話者もいるので、普通に書く機会は探せばあります。

 

あと、単純に話のネタとしてウケがいいというのもあります。アボカドとかワカモレとかコヨーテの語源の話をするもよし、実はスペイン植民地時代のメキシコでは結構長い間ナワトル語で裁判とかの行政をやっていた話をするもよし、フランス語の文法書がこの世に生まれるよりも前にナワトル語の文法書が在ったという話もよし。

 

さらに、わたしは「人間の言語ってどういう挙動をするんだろう?」という問いに対するデータセットをなるべく脳内に蓄えたいというモチベがあるので、語学書とかを買いまくって乱読したりしているのですが、

こういう文法書の乱読をして「面白かった〜」とだけなって単語を 3 つだけ覚えて終わるような語学の向き合い方だけではなく、テストを課されて単語暗記を強要されて真面目に文を読み書きできるようになる語学というのをやれたのは良かったと思っています。

 

よし、現在 23:59!

*1:というか、古典ナワトル語とワステカナワトル語。他の方言では区別するところもあるらしい

京大マイコンクラブ #hsjoihs-lifelog チャンネル

この記事は KMC Advent Calendar 2022 の 17 日目の記事です。16 日目の記事は itoooak さんの「PDFを書いてHelloWorldする」だそうですが、私がこの記事を執筆しているタイミングではまだ投稿されていません。

概要

それは 2021 年 11 月 9 日に唐突に始まった。京大マイコンクラブ(以下 KMC)の自分用チャンネルに、何気なく睡眠とかのログを取り始めたのである。

 

 

そのうち、それ以外の日常の出来事も全て記載していくようになった。

 

こういうのは一度始めると続いてしまうもので、そこから半年経っても、

 

一年経っても、私は自分の生活ログを取り続けた。

 

ちなみに、「こういうのは一度始めると続いてしまうもので」についてはもう一つ心当たりがあり、私はいま Duolingo の streak を 628 日繋いでいる。Duolingo の streak は一日サボっても切れない仕様で、私が自分でつけている集計によれば、「2021 年 2 月 19 日に Duolingo を始めてからの 667 日のうち、94.15% にあたる 628 日は Duolingo に触れている」というカウントになる。

docs.google.com

 

コツ

もちろん、なにかをしている最中に常にログを取っていけばいいのだが、必ずしもそういうわけにもいかない。よって、「後でログを取る自分」の手間を最小限にするには、以下のポイントを押さえるのが有用である。

  • 履歴を日単位でしか出してくれず、閲覧時刻を表示してくれないブラウザである Safari をなるべく使わない。使うにしても Twitter(投稿時刻・リツイート時刻が twilog に保存される)かニコニコ動画(視聴時刻が保存される)だけにする
  • なるべくあらゆる時間をインターネッツに注ぎ込み、「この時間ってなにしてたんだっけ?」という使途不明時間を減らす
  • 食事は必ず写真を撮ってDiscord や Slack のチャンネルに投稿
  • 眠くなったとき・起きたとき・外出したとき・外出から戻ったときにはその旨を特定 Discord サーバーの特定チャンネルで宣言する

これらのコツを駆使することで、2 日〜 3 日ほどログ取りをサボっても後からそれなりに自分史を追えるようになる。

 

今学期

さて、卒業が近く、しかも私は 3 種類の卒業要件を全て満たすようなスケジュールで動く必要があったため、

今学期はそれなりに忙しく、KMC にもそんなに顔を出せていなかったが、それでも hsjoihs-lifelog は執念で更新し続けた。

 

しかし…

おや、12 月 1 日で更新が止まってしまっている。

 

そう、12 月に入ってからというものの、タスクが積み上がり、忙しさが MAX に到達し(MAX と言いつつ、知り合いが「原神」中国語版の読解をするイベントとかには参加してたりしたけど)、ついに #hsjoihs-lifelog の更新が止まってしまったのだ。

 

 

ちょうどいま卒業に必要な重めの課題を提出し、あとは飛行機に乗って日本に帰るだけという解放感の中で記事を書いているが、なにはともあれ、とりあえず最大の忙しさは通り過ぎることができたので、今から 17 日分遡ってライフログ更新かねぇ………………(でもフライト寝過ごしたらシャレにならないので、この記事を投稿したら寝る)

架空伝統ゲーム「机戦」のオンライン対戦プラットフォームである cerke_online のコード負債 (part 0.5)

この記事は筆兵無傾 Advent Calendar 2022 およびボードゲーム・パズルプログラミング Advent Calendar 2022 の 14 日目の記事です。日本時間だと遅刻ですが、カリフォルニア時間だと現在 12 月 14 日の 25 時 12 分………

えっと、ハワイ時間ってことにしておきましょう(ハワイ行ったことないけど)。それならあと 47 分の猶予がありますね。

 

 



 

前回のあらすじ

hsjoihs.hatenablog.com

  • 異世界ファイクレオネで遊ばれている(という設定の)架空伝統ゲーム「机戦」の製作を手伝っています
  • オンライン対戦プラットフォーム「cerke online」を事実上全て一人で実装しています
  • 一人で突っ走っているので端的に言ってコードがカスであり、私一人でしか書いていないので気兼ねなく強い言葉でコードを罵ることができます

過去編

現状のコードにも様々な問題があるのですが、過去のコードが抱えていた(そして今では部分的に直っている)問題の方が見た目が面白いので、今回はそれをネタにしていこうと思います。

 

(過去の負債を強い語気でネタにするのに敬体はなじまないので、以下常体で書く)

 

一年前、具体的には 2021 年 11 月 6 日に、「いよいよソースコードがマズくなってきた!そろそろ先延ばしにするのをやめ、問題に直面しなくては」という思いのもとで、ファイル間の依存関係をグラフにしてみたときの様子がこちら。

 

おしまいである。スクショ中の「うわぁ。」「どうしてこんなになるまで放っておいたんだ。」が当時の私の気持ちを如実に表している。なんなら、1 年以上経った今でも、当時これを整理してまとめを書いたときの寮のベッドの配置が脳裏にはっきりと浮かび上がる*1

どうしてこうなってしまったのか

前回の記事でも書いたように、「今となって考えれば MVC パターンを採用しておけば遥かに見通しの良い実装となった」が、「当時は Elm も React もやったことがなく」組みやすいところから行きあたりばったりに組んで雑な増改築を積み重ねていった結果であるのはもちろんなのだが、もう一つ、そもそもこの机戦というゲームにそれなりに状態遷移が多く、必然的に API が複雑にならざるを得なかった(し、初めて組んでいる際には全体像が見えず、その複雑なものをきれいに構造に落とし込むという設計をサボってとりあえず書き始めてしまった)という事情がある。

 

ちなみに、上の図で現在 GroundState や ExcitedState と名前がついている型は、2021 年 10 月当時は「A」「C」とかいうカスな命名がされていた。

 

2021 年 12 月時点でこれらを GroundState や ExcitedState へと改称することも考えたのだが、当時の私は「物理学知らないとわかんない命名とか微妙でしょ」と考え、これを一旦却下

github.com

 

この判断を改め、『そういう命名は(属人化を上げるので)「闇鍋」になると言ったが、「物理学の素養があれば分かる命名」は、「誰にも絶対に分からない命名」よりも優れている。』として GroundState / ExcitedState を採用したのは、実に翌年の 2022 年 9 月を待つことになるのであった。

 

また、上の図で青矢印の横に書いてある POST 先が全て /decision/ の形になって一貫しているのは 2021 年 12 月 4 日にリファクタリングして API を整理したからであって、それ以前の命名惨憺たるものであった。

 

github.com

 

教訓

  • 一人で開発していると、得てしてコードは闇鍋化する
  • 他人が覗いてきたり他人に見せようとしたりしない限り、闇鍋はいくらでも拡大する
  • 年に 1 回ぐらいはコードのカスさを如実に示す文書を書いて反省に資するなどする必要がある

 

よし、現在ぴったりハワイ時間の 23:59!

*1:スタンフォードは住む寮が毎年変わる

架空伝統ゲーム「ケセリマ」を Elm で実装した話

この記事はボードゲーム・パズルプログラミング Advent Calendar 2022 と Elmのカレンダー | Advent Calendar 2022 および筆兵無傾 Advent Calendar 2022 の 12 日目の記事です。

背景 part 1

(記事の技術的内容には本質的な関わりがないので、お時間のない方はこのパートは読み飛ばしていただいても構いません。)

こんにちは、hsjoihs です。

過去記事にも書いた通り、創作の種類の一つに「架空世界創作」、つまり、架空の世界の言語・文化・地理などを創作するという形態があり、ご縁あって、2017年頃から私もファイクレオネという架空世界についての創作を手伝わせていただいております。

hsjoihs.hatenablog.com

具体的には、架空世界の言語を創作し、その言語で説話を書いたりしつつ、

架空のボードゲームの細かなルール差が生んでいる社会問題をその言語で語り、

f:id:hsjoihs:20211222185515j:plain

y1 huap1 cet2 kaik zui1(官定机戦論)。机戦にある地方ごとの細かなルール差が金銭トラブルなどを生んでいる実情を重く見て、「統一規則を制定することで人々の相互尊重が期待できる」「ルールの差を論じるにも、統一規則があると便利である」といった趣旨の文章。

それを元に公式の統一ルールを整備し、それをこれまた架空世界の言語で書き、

AIL PANIT LETI CETKAIK LETI KULANTE(アイル統一机戦書)の一ページ

そのボドゲオンライン対戦バージョンを実装していたりします。

f:id:hsjoihs:20211222184715p:plain

オンライン対戦の入り口ページで、言語設定で異世界の方を選んだ場合

背景 part 2

(記事の技術的内容には本質的な関わりがないので、お時間のない方はこのパートも読み飛ばしていただいても構いません。)

さて、このように、架空世界創作というのは、ゆったりまったりダラダラと何年も続けていくことのできる趣味であり、世界というものが壮大であるがゆえに無限に続けていくことができてしまうタイプの趣味です。

それはそれでいいのですが、逆に時間を区切って「一ヶ月でどこまで架空世界創作ができるか」という方面も面白いのでは、という話が持ち上がっていました。ちょうど小説投稿サイトノベルアップ+が「異世界ファンタジー設定コンテスト」というというのを2021年6月末締切で開催していたという経緯もあり、このコンテストの趣旨にあんまりそぐわないのを重々承知の上で、我々が今までやってきた

を整備するという作業を締切厳守でやってみよう、と考えてみたのです。

novelup.plus

 

なお、「ケセリマ」という名前の由来については、これまた過去記事に書いたのですが、

hsjoihs.hatenablog.com

 

hsjoihs「この前無意味語をパッと思いつく必要が発生し、その際に「ケセリマ」でググったら一切ヒットしなかった。ラテン字表記は Queserima とかでいいか。」

SY「ケセが光という意味でリマが闇という意味です。」

hsjoihs「じゃあそれで。」

 

というスピード採用によるものです。

Elm を採用したわけ

過去記事にも書いたように、架空伝統ゲーム「机戦」のオンライン対戦では生 DOM を弄り倒すコードを書いていったので、全体像を追いづらかったという事情がありました。

hsjoihs.hatenablog.com

 

今までフレームワークを使ってのフロントエンド開発をあまりしたことがなかったという経緯があり、別の Web アプリを雑に組んでみようと思った際に「今度こそはフレームワークを使おう」と考えました。こりーさんと話した際にその旨を口にしたところ、

となりました。

そこで、2021 年 4 月 5 日の新幹線内で特にやることがなかったので、「せっかくなら前々から興味のあった Elm を学ぶか」と思い実践してみました。

その結果、The Elm Architecture がことボードゲームを書く上でかなり適しているということを実感しました。「机戦」の実装は既にコード負債がだいぶ積み重なっており、そもそもゲームそのものがかなり複雑ということもあって、現状これを別フレームワークへと移し替える目処は立っていないのですが、今回「ケセリマ」を一から実装するにあたっては Elm を使ってみようという気になりました。

採用してみての感想

幸い、中学生の頃に情報オリンピック夏季セミナーで Haskell を習っていたこともあり、基本的に特に困ることなく実装していくことができました。

また、実装する対象がボードゲームなので、Model - View - Update からなる The Elm Architecture がかなり上手く適合し、非常に書きやすかったです。

github.com

 

エスケープハッチが用意されておらず、部分関数を用いない「正しい」実装を強いられるというのも、なかなか良い体験でした。

 

ということで、Elm を使ってもう一度ボードゲームを 2022 年 3 月に実装することになるのですが、まあそれについては別の機会に話せればと思っております。

今後の展望

一方で、https://lukeplant.me.uk/blog/posts/why-im-leaving-elm/ とかを見ると、

  • 内部的で不自由な特権機構が形成されてしまっているっぽい
  • コミュニティ運営やコントリビューションに関するメタプロセスがしっかり整備されていないっぽい

(これはこりーさん言語化で、かなり私の感想と合致しているので引用させてもらう)

 

という事情があるらしく、「Elm そのものはフレームワークとしてせっかくの素晴らしさがあるのに、それを帳消ししてしまっているなぁ」という気持ちも若干……(React が Elm のいいところを上手く回収してくれているからという事情もある。書いた HTML タグをドキュメント見ながら全部 Elm の関数に手作業で翻訳していく作業を経た結果、後に React を使った際に「JSX って便利だなぁ」という感想になったし)

架空伝統ゲーム「机戦」の AI を強くするため、盤面を効率よく保持する

この記事は筆兵無傾 Advent Calendar 2022 と ボードゲーム・パズルプログラミング Advent Calendar 2022 の 7 日目の記事です。日本時間だと遅刻ですが、私はいまアメリカ合衆国にいるので遅刻ではありません(ズルい)。

 

経緯

さて、両アドベントカレンダーの 3 日目の記事である ↓ で、 id:primenumber が以下のような記事を書いてくれました。

primenumber.hatenadiary.jp

 

これをもとに作業していただいた結果、「1〜2桁くらい速くなるとできることだいぶ増えそう」と言われました。

そすうぽよ — 2022/12/04 20:43 「@hsjoihs 1〜2桁くらい速くなるとできることだいぶ増えそうなんですが、どれくらい頑張れそうな予感とかありますか?」hsjoihs — 2022/12/04 20:50
「『今月の前半は卒業のために必要な作業が存在し、その作業よりもたのしい最適化作業を頑張りすぎると、卒業のための作業が崩壊する』という点を抜きにすればめちゃめちゃ頑張れます。ところで、卒業は大事なので、今月の前半は頑張りをほどほどにするよう自らを律する必要があります」そすうぽよ — 2022/12/04 20:51「🆗🆗」

 

現状の実装は「まあ速いとうれしいけど、速さだけを求めるのは本当に速さが必要になってからでいいよね」というモチベで 2 年前に TypeScript 実装から手で移植したもののはずで、そんなに効率的な実装にはなっていませんでした。

github.com

この issue にあるように、「(将棋の角や飛車、チェスのルークやビショップのように)長距離移動する駒の動きを別の駒が妨げているかどうかを知りたい。どのマスを見ればいいか」というのを計算する関数が少し遅く、しかもこれは非常に呼ばれる回数が多いため、全体の実行時間をかなり占めてしまっていたとのことでした。

 

提案に基づきここを改善したところ、次は

そすうぽよ — 2022/12/05 02:17「 これで全体が1桁速くなるというわけではなくて、様々な最適化をがんばって2桁ぐらい速くしませんか?という話です。次のボトルネックは順にrotate board, malloc, freeですね」hsjoihs — 2022/12/05 02:19「おー、rotate_board(たしかにあまりにも多そう)の次は malloc と free がボトルネックになる感じですか。了解です」

 

これはどういうことかというと、数年前に TypeScript 実装を書いたとき、「片側から見たときの駒の動きを実装し終えた。もう片側も実装するとなると面倒なので、逆側から入力が来たら、盤面を回転させて関数を呼んで結果をもう一回回転させよう」という横着をしたのが原因で、AI が手を計算しているあいだ盤面が一生クルクルしており、そのクルクルで全体の 27.24 % の実行時間を消費してしまっていたのでした。

 

「それぐらい予見できただろ、なんで横着をしたんだ」と思われるかもしれませんが、この「机戦」というゲームは

  • 駒を踏んでそこからさらに移動する動きがあり、踏む駒の位置とそこから移動する目的地を順に指定させねばならない
  • 指し手を決める途中でサーバーに乱数を問い合わせ、それに基づき可能な手の候補を削り、クライアント側に返す必要がある
  • 到着地点が水色マスである場合、乱数を発生させ、出目不足なら指し手をキャンセル

などと、遷移する必要のある状態数がかなり多く、それらを全てバグらせずに組むのがあまりに面倒だったので、最大限に横着をしたかったのです。

 

そうは言っても、遅いせいで AI の開発の妨げになっているならよろしくない。ということで、コードを調べ、いろいろいじり、rotate_board を削除しました。

 

問題

とはいえ、そもそも速く実行できるようにすることを意識していないコードなので、それをリファクタリングしていって速度を上げていくというのにも限界があります。現状の遅い実装はリファレンス実装とし、「メモリへの優しさを最優先した構造へと書き換える」をやらないといかんという結論に至りました。

そすうぽよ — 昨日 05:29 「こっちの環境ではmalloc, cfree, ...の順になってて再現できてないんですよね」 hsjoihs — 昨日 05:52 「WSL で走らせてるのが変な影響出てるのかなぁ まあとりあえずいずれにせよ『メモリへの優しさを最優先した構造へと書き換える』をやらないといかんのは容易に想像が付きますね」そすうぽよ — 昨日 06:04 「9*9の盤面に駒を並べるデータ構造にするか、49駒それぞれの位置と状態を記憶するか」

 

49 駒それぞれの位置と状態を記憶すると、49 バイトで必ず収まるというメリットがある一方で、このゲームのゲームロジック上では「特定のマスが空きマスかどうか」を判定する機会が多く、それがいちいち面倒となるデータ構造ではよろしくないと考えました。

一方、このゲームには持ち駒の概念があります。よって、そちらについては「48 駒それぞれについて*1、どちらかの持ち駒になっているか、それともどちらの持ち駒でもないのか」を保持する、という方針にしました。

紆余曲折

諸事情*2により、空マスを表す番号は 0 にしたかったので、最初はこのような駒番号割り当てを考えていました。

 

 

この番号の振り方は、伝統的事情とコンピュータ的事情を融合させた割り当てになっています。というのも、id:yasusho1020 が手作業で机戦の駒を彫るときには以下のような工程を経るのですが、

 

この字母紙が以下のような配列になっているので、それを踏襲して順序を決めました。

また、赤黒を交互にして縦列の順で読んでいくというエンコーディングにすることで、

  • 最下位ビットで色
  • 不等式評価で役職

が手に入り、コンピュータにやさしい(素早く計算できる)、という考えがありました。

 

しかしながら、id:primenumber から「兵が1スタートでなく0スタートにした方がbit演算とか表引きとかに便利そうかなあと思いました」という意見がもたらされ、「そうだよなぁ、どうしようかな」と思っていたところ、非常に上手いアイデアが寄せられ、

 

そすうぽよ — 昨日 22:33「 兵が1スタートでなく0スタートにした方がbit演算とか表引きとかに便利そうかなあと思いました」hsjoihs — 昨日 22:39「これ迷って、Option<NonZeroU8> で書けるように 1 スタートにしたんですけど、やっぱ 0 スタートのほうがいいですかね」そすうぽよ — 昨日 22:42「64スタートにして上位2bitが0b00→どちらも動かせない(空きマス) 0b01→IASideが動かせる 0b10→ASideが動かせる 0b11→どちらも動かせる(皇)にするとかもありかも」hsjoihs — 昨日 22:42「あ〜〜上手そう 持ち駒情報の格納のしかたとも整合するわな」

これを採用することにしました。

 

結論

以下のような番号割り当てを採用することにしました。(0o は 8 進数を表す表記)

盤面に関しては 9 × 9 の空間に直に駒番号を書いていき、持ち駒に関しては、例えば下のような盤面については持ち駒を右図のように抜き出し、それぞれの枠に

  • 上向きなら 01 
  • 下向きなら 10
  • どちらでもないなら 00

という 2 桁を書き、2 進数 96 桁で 12 バイトとする、という方針に決めました。


この方法だと、盤面全体が 81 + 12 = 93 バイトに収まるだけでなく、様々な演算をビット演算で書くことができて機械にやさしいです。

 

以上の話は ↓ のリポジトリにまとめてあります。

github.com

 

今後の展望

学期末を乗り切り、冬休みに入ったら、これらをもとに可能手を爆速に列挙できるようにしたいですね。まあ既存実装をほぼそのまま移植することになりそう。

*1:49 駒のうち 1 駒は、ゲームシステム上持ち駒になることが絶対にありえない

*2:Rust で Option<NonZeroU8> が 1 バイトになるというのを使いたい