2021年12月26日日曜日

早口音読とダジャレ音読を作った

早口音読ダジャレ音読 を作りました。 小学校でよく宿題になる音読ですが、 短文を少し読むだけのケースも多い気がしてます。 それだったら面白い音読をたくさんしてもいいのでは、と思いました。 という訳で、早口言葉とダジャレを音読練習しましょう。



使い道はわかりやすいですが、難点があるとすれば、 早口言葉は小〜1小2の子には漢字や言葉がすこし難しいことでしょうか。 小2ならギリギリいけそうかな…。小さな子はダジャレのほうが良さそうです。

ダジャレ成分が高いので、ひさびさに Zenn にもネタ記事を投稿してみました。 紹介する方法では非常にコンパクトなデータ構造を使い、総当たりに近い形で読みを探索することで、音声認識の表記揺れに対応します。 実装上の改善ポイントはいろいろあって、読み辞書の生成はもっときちんと解析すれば、さらにコンパクトにできそうです。

技術的な話 (長音符)

Zenn には書いてない技術的な補足記事も書いてみます。 総当たりで解析しても、それでも解析が難しいところもあります。 それは「あー」「ああ」「あぁ」の違いです。 無理そうなので除外したりしている例をいくつかリストアップすると以下。
土星が驚いた。ドシェー!,どせい が おどろいた。ドシェー!
エイがイェーイ,えい が イェーイ
栄養は体にえーよー,えいよう は からだ に えーよー
英和辞典はええわー,えいわ じてん は ええわー
エースでえーっすか?,エース で えーっすか?
あの歌の歌詞ははずかしー,あの うた の かし は はずかしー
キタねえやつが来たねえ,キタねえ やつ が きたねえ
キタねーやつが来たねー,キタねー やつ が きたねー
交代するなんてなんてこうったい,こうたい する なんて なんて こうったい
高知にいるからこうっちに来い
コート来ておこーっと,コート きて おこーっと
屋根はやーねー,やね は やーねー
Google の音声認識だと、良い候補がなかった時には「よくわからんから一番近い言葉に変えてしまおう」という感じで動きます。 「えーよー」は「えいよう」になります。「ー」は置換される傾向があります。 「おこーっと」は「おこうと」となります。「っ」は消滅する傾向があります。 「やーねー」のような綺麗な文章で存在しない言葉は「やね」に丸められます。 これらの変換に音声認識側でルールはないように見えます。 ちなみに 長音符 「ー」 に文法的ルールはあって、 現代語ではひらがなに長音符は使わないように教育されます。 音声認識にも一定量は反映されていると思います。

上記を踏まえると、ダジャレは丁寧な文法で作る必要があります。 「っ」と「ー」「ぁぃぅぇぉ」が出てきたら、認識チェックは必須です。 「は」と「わ」も注意が必要です。 「だぜー」「いたいよー」「だぜっ」と言った口語特有の表現は、Google の音声認識では、ほぼ長音符なしに変換されます。 なので文末の一文字だけ削除してチェックすると案外ちんと動くことがあります。 ただ「うめー」「だお」のように長音符ありに変換されやすいものもあります。 このへんの違いは何とも言えないんですが、日本語は語尾が小さくなるように話されるので、最後の一文字が長音符なら判定はゆるくて良いのかなと思ってます。

ちなみに「っ」と「ー」「ぁぃぅぇぉ」が同時に二つ出てきたら、まず認識できないと思ってます。 たとえば「イェーイ」ですが、これは人間でも文字起こしが厳しいですよね。 解析側で処理しようとしても「お」が「う」に変わったり、「え」が「い」に変わったして大変です。 他にもダジャレ特有の、微妙に音韻がズレているもの (ex: おっかしいなあ→おっかりーなあ) を一緒と判定する機能は作ってません。 今回は対象がオジサンではなく小さな子なので、綺麗な文法のダジャレだけに限定して良いと思ってます。 今回は実装していませんが、長音符に起因する表記揺れはもう少し対応できるようにしても良いかも知れません。

技術的な話 (連濁)

人間には簡単に認識できても、妙に音声認識率が悪い言葉もあります。 例えば「勉強好き」の「ず」のように濁音になるケースの処理が難しいです。 こういうのは「連濁」と呼ばれるらしいです。 ちなみに「べんきょうすき」と言わないと認識してくれません。 理由を考えると「づき」「ずき」のように、同じ発音にひらがなが複数あるため、発音が一緒の熟語が出てきてしまうからだと思います。 読み解析側ではサポートしてますが、音声認識側の精度によって読み方を指定しないと苦しいダジャレがいくつかあります。 STT はこういった細かい部分を全部チェックしないと不安が残る精度なので、バリエーションは増やしにくいです。

音声認識の課題

作ってみて一番気になったのは既存の音声認識の、非言語的フレーズへの弱さの問題です。 たとえば九九の音読も作ろうと試みたのですが、 Google の音声認識は文章的なものの認識率はすごい一方、それ以外の精度は低くてうまく認識しません。 その原因は、認識結果を見る限り、統計的に起きやすい文章と発音に無理やり解釈してまう (ように見える) からです。 現時点では細かな部分は聞き取りミスしやすいものをすべて登録するような荒業以外に解決策がないです。 九九などはそれでも対処できないくらい正解から外れたものばかりなので、頑張っても苦労が増えるだけです。 音声認識の精度がもっと上がるか、文章以外も認識できるモデルが出るまでは待ちですね。

形態素解析の課題

形態素解析辞書についても、普通の形態素解析では見つからない細かな問題が見つかり、なかなか面白かったです。 古語や口語の対応が難しかったり、日常的に使われる特殊な読み、日常的に省略されたり追加される語尾の存在など、色々と気付かされました。 mecab-ipadic-neologd でもそういった言葉は登録されてなかったので、改善はできなそうです。 UniDic は多少の登録がありますが、ほんの少しの改善が限度になりそうです。 あと長音符や連濁をうまく扱えるライブラリがあるといいですね。 先に正規化してから形態素解析すればスコアは結構上がるんじゃないかなとも思ったりします。

個人的には世の中の多くのタスクに N-Best な形態素解析は不要だと思っていて、 そんな気持ちを少し前にブログにも書きました。 その一つの答えになっている仕組みは実現できているんじゃないかと思います。 ちなみに最近流行の BERT でポンも解決策の一つですが、メモリや速度や資本に問題が生じやすいです。 最近流行のスケーリング上げればどうとでもなる世界線なら、なおさらです。 深層学習の構造上、現時点ではメモリや速度の最適化あたりはまだ人手がいる気がしています。 面倒だから基本やりたくないけど、アプリを作ってるとたまに必要になります。

2021年12月24日金曜日

フリゲ紹介: Super Depth

私が人生で最初にプレイしたゲームの名前をひょんなことから思い出せたので、メモしておきます。 名前は Super Depth といって、PC-98 でプレイできたゲームみたいです。

当時プレイしたのは古すぎてパソコンが捨てられる寸前で、小1くらいの時だったのかなあ。 もう少し前かもしれないし、後かも知れません。 PC-98 が捨てられてからは、信長の野望をプレイしていたことをよく覚えています。 キーボードも適当に押して、手探りでプレイしていた記憶があります。 英語とか絶対に何もわかってないはずなので、少しくらいなら何とかなることがわかりますね。

Super Depth は当時もめちゃくちゃ面白かった記憶があって、ゲーム画面をよく覚えていました。 実際 PC-98 世代の人には大人気だったゲームらしいです。 今の世代の人がプレイしても、ミニゲームとしてなら十分楽しめる完成度だと思います。



Super Depath は、Android と Flash に移植されネット上で公開されていて、自由にプレイできます。 Linux 上でプレイしようと思ったのですが、Anbox 経由で Android 版をプレイするのは少し面倒です。 Flash の SWF をダウンロードしてFlash Player projector でプレイするのが一番楽そうです。

もはや何年越しかわからないくらい久々にプレイしてみても、よくできているなあと感じます。 最近のシューティングは派手なのが多いけど、Super Depth はシンプルなのに奥が深いのが良いですね。



昔プレイした時よりすこし弾速が早い気がする。ジャネーの法則?

2021年12月11日土曜日

九九タイピングを作った

九九タイピング を作りました。

かけ算は早く計算することを目的とした作業なので、 ほとんどの子は 手書きde暗算タイプde暗算 のほうが練習に良いです。 ただ集中力がなくて続かない子はそれなりにいます。 また、タイピングも ABCあいうえお は全員がすぐクリアできますが、 次の 漢字フォニックス あたりで苦労する子は多いです。 そんな時に使います。



フォニックスあたりで苦労する要因は以下と勝手に思ってます。
  • 単純に練習量が足りない
  • 題材が少なく飽きやすい
  • ホームポジションがいい加減
  • 小さな子ほど集中力が低く飽きっぽい

あまり良い解決策はないので、ネタを追加するのが一番じゃないかな。 4つとも改善するには九九は良いかもと思って作りました。 フォニックスより文字数が増えるのが難点ですが、 決まったフレーズを早く入力してホームポジションを意識してもらいながら、 飽きやすい1-2年生に多少なりとも題材を増やすことができます。

ネタ用という感じですが、ときにはネタも必要なので、まあいいかな。 九九を丸ごとタイプすると 5key/sec でも段ごとに 20〜25秒くらいは掛かります。 1key/sec が出ない子が遊ぶことが多いと思うので、100〜200秒は掛かることを想定したほうが良さそうです。 そのような子にとって 200秒は厳しい気がしたので、答えだけ入力するようにしてみました。 これなら 5key/sec で 5〜15秒くらいになります。 あいうえおよりほんの少しハードルが高いくらいの難易度になり、 1key/sec が出ない子でも 25〜150秒で終わる計算になるので良い調整になります。

英語で読み上げたら面白かったので、あえて英語の読み上げにしました。

2021年12月1日水曜日

Lubuntu + WineHQ で快適なフリゲ生活

WineHQ が進化してほとんどのゲームが動くようになりました。 インストールがすごく簡単になって色々変わったので、WineHQ 6.0.2 + Lubuntu 20.04 の綺麗なワンパスを残しておきます。 現時点ではほぼ完ぺきな設定になっていると思います。

WineHQ のインストール

まずは公式の説明通りに WineHQ をインストールします
sudo dpkg --add-architecture i386
wget -nc https://dl.winehq.org/wine-builds/winehq.key
sudo apt-key add winehq.key
sudo add-apt-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ focal main'  # 20.04
sudo apt install --install-recommends winehq-stable
最近の WineHQ は、実のところこれだけでほとんどのゲームは動きます。 Mono, Gecko などのわかりやすいパッケージは、アプリ起動時に足りなければ自動でインストールもしてくれます。 ただそこから多少の設定は必要ですし、最初の設定をミスると動きません。 まずは速攻で以下のおまじないを掛けることが大切です。 おまじないは ~/.bash_profile に設定しておけば、ファイラーからゲームをクリック実行できます。
export MESA_GL_VERSION_OVERRIDE=4.5
もう一つ大事なことは、WineHQ の動作モードです。 WineHQ は 32bit / 64bit の 2つのモードで動きますが、 32bit だと一部のゲームが動かなくなってしまうので、64bit で動かすべきです。 警告がたくさん出ますが、64bit モードで動かせば、32bit にも自動対応してくれます。 なおデフォルトは 64bit なので特に気にする必要はありません。

MESA 関連の設定

先程の設定は公式フォーラムでも紹介されていたテクニックです。 しかし環境変数を export してしまうと OpenGL や MESA を利用したアプリケーション全体が動かなくなる可能性があります。 たとえば Google Chrome などが動かなくなる問題があります。 問題となる場合は、env + alias コマンドを代用して、Wine だけ MESA 4.5 で動くようにしましょう。 先ほどの設定の代わりに、~/.bashrc に以下を設定します。
alias winehq="env MESA_GL_VERSION_OVERRIDE=4.5 wine"
この設定だけだとファイラーからクリック実行できないので関連付けの設定もします。 /usr/share/applications/wine.desktop の Exec フィールドを以下のようにしましょう。
# before
Exec=wine start /unix %f
# after
Exec=env MESA_GL_VERSION_OVERRIDE=4.5 wine start /unix %f
これでファイラーからの安全なクリック実行もできるようになりました。

Winetricks の設定

さて、先ほどゲームは動くと書きましたが、実際には文字が汚くて読みにくいので、以下のパッケージをダウンロードします。
sudo apt install winetricks
winetricks fakejapanese_ipamona  # MSゴシック 代替
昔は、さらに quartz, directmusic, gmdls などの動画・音声周りのインストールが必要でしたが、 今はインストールすると逆に不整合が起きて動かなくなってしまうようです。 特に MP3 周りが危険みたいで、「未実装です。」のエラーが出るようになってしまいます。 外部依存性のあるパッケージだけ追加が必要で、それ以外は設定がいらないように分けられているのは、わかりやすくて良いですね。

MIDI のインストール

さらに MIDI をインストールします。 私は最近ようやく GUI なしで動かせるようになりましたので、その設定を書いておきます。 サウンドフォントは /usr/share/sounds や /usr/share/midi にインストールされます。
# サウンドフォント
sudo apt install freepats
sudo apt install fluid-soundfont-gs fluid-soundfont-gm

# 音声出力
sudo apt install fluidsynth
sudo apt install timidity timidity-daemon 
sudo apt install alsa-utils

# 再生設定1
echo "timidity -iAD -B2,8 -Os1l -s 44100 -x 'soundfont /usr/share/sounds/sf2/FluidR3_GS.sf2 order=1' > / dev/null 2>&1 &" >> ~/.bash_profile
# 再生設定2
echo "fluidsynth -l -s -i -aalsa -o audio.alsa.device=default /usr/share/sounds/sf2/TimGM6mb.sf2 > /dev/null 2>&1 &" >> ~/.bash_profile
source ~/.bash_profile
いろいろサウンドフォントを試してみましたが、fluidsynth の中に入っている TimGM6mb.sf2 が一番綺麗です。 音声出力と再生設定は timidity と fluidsynth のどちらかだけで OK。 そのため、最小構成でインストールするなら fluidsynth と alsa-utils だけで良さそうです。 ~/.bash_profile に MIDI を設定しておけば、デスクトップからダブルクリックでフリゲを起動できます。

再生設定の & は書き忘れるとデスクトップにログインできなくなるので、編集するときはそこだけ要注意してください。 /etc/init.d に登録したり手動起動のほうが安全かも知れません。 インストールに関しては、以上。

最近の動作状況

OS のメモリ使用量の影響もあると思いますが、全体的に Windows よりサクサク動きます。 今や「Wine だと遅いのでは」といった心配は無用で、とても快適です。 ついでなので、以下のページのリスト (日々更新しているのでもう少しチェックしてる) と、有名作品を 10こくらい動作確認してみました。
ゲームの最初をプレイしてみて、動かなかった作品は一つもありませんでした。 ASTLIBRA なんかも普通に動きます。すごい。 ちなみに今回の検証で一番ハマったのは、Wine そのものではなく、Alacritty から起動するとなぜか Wine が動かなかったところ。 今もよくわからないですが、まあ MESA 関連かなあ。

ゲームごとの解決策まとめ

ここまでの説明でほとんどのゲームは動きますが、動かないものもあります。 動かないアプリを動かすための Tips をいくつか載せておきます。
# Win' Wind' Windy
# Trial Windy
# error: 0024:err:gstreamer:mpeg_splitter_init_gst Failed to create mpegaudioparse; are 32-bit GStreamer "good" plugins installed?
sudo apt install gstreamer1.0-plugins-good:i386

また非常にハマりやすい (というかハマった) ポイントを挙げておくと、 巷のゲームの zip ファイルを unzip -O cp932 hoge.zip で展開して遊ぼうとすると、 RPG ツクール 2000 系のゲームではディレクトリの権限がおかしくなることがあります。 RTP をインストールしているはずなのに「ファイルが開けません」と出てきたら、だいたいコレです。 そんな時は以下のコマンドで権限を正しいものに戻しておきましょう。
# RPG_RT.exe があるディレクトリで以下を実行
chmod 755 */

氷水火山SOKOBAN は winetricks を使わないと動きません。 今のところ気になるのはこれくらい。 dsound などを入れれば動きますが、入れてしまうとメイジの転生録などの MP3 を使うゲームが動かなくなります。 MP3 が再生できないと困るアプリのほうが多いので、dsound は入れないほうが良いと思います。 現状では MP3 絡みのところを以下のようなスクリプトを作っておいて切り替えるのが良さそうです。
#!/bin/bash

dir=$(cd $(dirname $0); pwd)
cd "$dir"
env WINEPREFIX=~/.wine-dsound wine RPG_RT.exe

Linux でフリゲの時代はもう来ていますね。

2021年11月15日月曜日

学年別の熟語辞書を作った

学年別の熟語辞書 を作りました。 というか形態素 N-gram からの良い熟語生成を思い付いてしまったので、 以前作った ngram-idioms を一から作り直しました。



以前の生成方法

ngram-idioms では、 形態素 Ngram を前後のつながりを見ながら形態素を複数つなげて熟語らしいものを作り、 テンプレートマッチングでノイズを除去していました。 しかしこの方法は面倒だし、形態素 N-gram の gram 数の影響を受けて、 頻出熟語しか抽出できない問題があります。 今までのような教育用途だと問題ない精度で抽出できるのですが、手作業のノイズ除去が必要だし、 数は少なくなるし賢い生成方法ではないです。

改良案

どこまで真面目に作るかの問題があるのですが、シンプルに作るなら以下の方法で良さそうです。 手作業のメンテが不要なのが良い。
  1. 日本語ウェブコーパス 2010 の 1〜7gram から熟語っぽいものを生成
  2. SudachiDict で熟語判定
  3. SudachiDictinappropriate-words-ja で地名と人名と禁止語を削除
  4. 品詞判定で不要語を削除
  5. 漢字レベルに合わせて形態素を分類

ポイントは形態素解析辞書の生起コストに違和感があるので、形態素 N-gram のカウントを使うところ。 形態素解析辞書は Neologd でも良いですのが、より信用できる SudachiDict にしました。 以下の記事は熟語生成にもとても参考になります。 なんだ今なら簡単に作れるじゃないかーと思いました。 まあ以前作ったときは SudachiDict の固有名詞フィルターがどれくらい使えるかわからなかったので仕方ない。 やっぱ色々素振りして遊んでないと駄目ですね。

難しいところ=熟語 or 複合名詞

熟語かどうかの判定はとても難しいです。 そのよくわからないところを形態素の 2〜7gram を見て語彙生成した後、SudachiDict にぶん投げて解決しています。 7gram にもなると早口言葉みたいなのがたくさん出てきます。

SudachiDict なら A〜B単位がまさに熟語となります。C単位は微妙なところ。 C単位は「正多角形」のような熟語がほんの少しある一方、「八戸市内」のような熟語か?というものが大量に混ざってきます。 熟語かどうかはすごく判断が難しくて、考え始めるとキリがないです。 そもそも「歴史」も「草木」も熟語じゃなくて複合名詞では?と言われれば「確かにそうかも」となるように、本質的に難しいです。 私は雑なので頑張らず、SudachiDict に丸投げし、B単位までにしました。

SudachiDict は本当によくできた辞書なので、全体的にいい感じに生成されているように見えます。 データを見てもわかるのは、三字熟語まではまあまあ人間的にもわかりやすいですが、 四字熟語以上は熟語なのかしっかり考えないとわからないもの (C単位) のほうが多くなってきます。 ゲームで使えるのは三字熟語まででしょうね。 また基本語彙を押さえておけば三字以上の熟語の大半の意味はわかることも確認できました。

ちなみに昔の実装も面白いので ngram-idioms は残してあります。 ngram-idioms では手動のフィルターを結構作ったので、差分の比較に便利です。 たとえば SudachiDictでは「花王」「山門」「太一」「東横」などを普通名詞にしていますね。 この中では「花王」は固有名詞が正しい気がします。 ただ企業名のフィルターはかなり難しいので、まあ無視で良いかな…。

このへんを頑張ろうとするなら、Mecab をフィルターにすれば、頻度を考慮するので名前として除外されます。 最初は Mecab を併用して頑張ってノイズ除去していましたが、 ビルドに時間が掛かりますし、SudachiDict の進化に期待して最近は特に除去していません。

かなり良いものができたので、以前作ったアプリで関係のあるものは全部更新しておきました。 まずは統計の歪みが完璧に消えて完成度が高くなったと思います。 また難易度調整もしやすくなり、ドリル系のアプリもかなり使いやすくなりました。 なるべく基本語彙から学んでいくことで学習効果も高くなったはず。

学年別の基本語彙一覧を作った

なんとなく知りたかったので、学年別の基本語彙一覧を作りました。 ようするに graded-enja-corpusmGSL の日本語版です。



作り方

どこまで真面目に作るかの問題があるのですが、シンプルに作るなら以下の方法で良さそうです。 手作業のメンテが不要なのが良い。
  1. 日本語ウェブコーパス 2010 の 1gram を正規化して語彙に変換
  2. 機械的に多少ノイズ除去
  3. SudachiDictinappropriate-words-ja で地名と人名と禁止語を削除
  4. 品詞判定で不要語を削除
  5. 表層形から原形に直した上で頻度順にソート
  6. 漢字レベルに合わせて形態素を分類

地名や人名のフィルターに SudachiDict を使ってますが、Neologd でも良いかも知れません。 もっとも Neologd は 2gram 以上でないと意味がないので、SudachiDictで 良いんじゃないかな。 処理自体は 2gram にも対応できますが、してません。 2gram を処理すると複合語をさらに抽出できる可能性がありますが、作りたかったのは「語彙一覧」ではなく「基本語彙一覧」です。 別にいらないかなと。 技術的には IPADic では UniDic を使ったほうが厳密ではないか、など色々細かい話はあるけど、まあ無視できるレベルでしょう。 IPADic の粒度のほうが実用上は便利です。

語彙と基本語彙

「語彙」という言葉には曖昧性があり、慣用句を含むこともありますが、 「基本語彙」と書いた時には形態素の原形を指している気がします。 「基本語彙」がわかっていれば「語彙」も、慣用句などの特殊ケースを除いて意味がわかるはずです。 今回作った一覧表は、先生が学年ごとにどれくらい「基本語彙」を覚えるべきか確認したり、 生徒が学年別にサラッと眺めて「基本語彙」を覚えてもらうために作りました。

扱いの難しいもの=接頭辞、接尾辞

この辞書には、固有名詞や複合名詞の他にも、単体では意味の通じない接頭辞や接尾辞は含まれません。 具体例を挙げると「一ヶ月」は複合語、「ヶ月」が接尾辞なので含まれません。 そこをどこまで真面目に含めるかという話はあるのですが、 (1) 接頭辞や接尾時は明らかに実用上ノイズのほうが多く、(2) 後からデータを活用しにくくなるので、削除することにしました。 接尾時や接頭辞は基本語彙じゃないの?と言われると、ぐぬぬ…ですが、別に辞書を作ったほうが平和と思ったので、勘弁。

接頭辞や接尾辞も考慮した辞書を作るには、2gram で複合名詞化すればいいです。 ただ基本語彙ではないように思うので、今回はやってません。 ひらがなはどこまでやっても微妙な出力ですが、こればかりは仕方なさそう。

2021年11月14日日曜日

漢字判定のいろいろな正規表現

漢字判定の正規表現は奥が深いです。 [一-龠] の記述がネット上にたくさんあるのですが、 「々ヵヶ」といった頻出語をサポートしてないことに気付きました。 みんな苦労しているみたい。 たまに必要になるので、自分なりの考えをメモしておきます。

結論はこちら。
[\u4E00-\u9FFF]        # CJK 統合漢字の基本多言語面
[\u4E00-\u9FFF𠮟]      # 常用漢字 (これもありかも)
[\u4E00-\u9FFF々]      # 実用上ここまではサポートしたい
[\u4E00-\u9FFF々ヵヶ]  # +頻出語
[\u4E00-\u9FFF々ヵヶヽヾゝゞ〃仝]  # +くり返し記号など
/[\u3400-\u9FFF\uF900-\uFAFF\u{20000}-\u{37FFF}]/u    # Unicode 15.1 漢字すべて
/[\u3400-\u9FFF\uF900-\uFAFF\u{20000}-\u{37FFF}々]/u  # Unicode 15.1 漢字すべて

漢字の指定

漢字の指定方法は、[一-龠] というものがネット上で多く見受けられ、私も利用していました。 最後の漢字が「龠」つまり U+9FA0 である理由は正直よくわからないのですが、たぶん Unicode 1.1 のときの指定を使っているのでしょう。 「龍」というよく使われる単語が、U+9F8D にあるので、それ以上の範囲削減は不可能です。 現在はさらに拡張されていますし、CJK 統合漢字の基本多言語面に基づく、[\u4E00-\u9FFF] が最も厳密な指定だろうと思います。

ちなみに [\u4E00-\u9FFF] の指定は JIS 第 2 水準までを想定した指定ですが、新常用漢字の「𠮟」を処理できないなどの問題があります。 ではどうするかというと、Unicode プロパティの /\p{scx=Han}/u を使う方法もあります。 ただこれでも「々」は判定できませんし、記号が含まれる致命的な問題があります。 同じことは /\p{scx=Hiragana}/u などにも言えて、Unicodeプロパティは意外と扱いに困ります。

ちょっと前に /[\u3400-\u9FFF\uF900-\uFAFF\u{20000}-\u{2FFFF}]/u という漢字判定もよく見かけました。 これもいい感じではあるのですが、今は CJK統合漢字拡張 H 以降に対応できていません。 そのため第3漢字面もサポートしないといけませんが、U+38000〜U+3AB9FF は甲骨文字がアサインされる予定があります。 甲骨文字も漢字と言えば漢字なのですが…漢字としてサポートするべきかは迷うところです。ひとまずサポートしないほうが良さそうかな。 そのギリギリ手前の U+37FFF までにするか、CJK統合漢字拡張 H の最終文字 U+323AF までにするかは難しいところです。 結局どこまでにするのが良いかは Unicode と CJK統合漢字の進化をよく見ながら決定するしかないのだろうと思います。

Unicode 15.1 (最新) の漢字すべてを想定した時に、現状でたぶん一番楽そうなのは、/[\u3400-\u9FFF\uF900-\uFAFF\u{20000}-\u{37FFF}]/u という漢字判定です。 Unicode フラグ u を付けた上で、\u{} で指定するのがポイント。

参考:

漢字に関連する語の指定

/\p{scx=Han}/u は使えそうで使い物にならないことが多いので、漢字や用例、文字コードに対する知識を深めるのは割と重要そうです。 くり返しを意味する「々」は漢字ではないらしいのですが、日常生活でも頻繁に出現します。 そのためたいていはサポートしないと、色々な不都合が生じます。

また「一ヶ所」のような使い方をする「ヵヶ」もたいてい必須語になります。 ただ「ヵヶ」は助数詞可能な名詞として出現し、必ず複合語として使われる漢字なので、サポートしなくていい場合もありそうです。 「ヵヶ」は Unicocde 的にはカタカナの一部として扱われているので、カタカナ語との重複に注意が必要です。 SudachiDict を見ると「ヶラヶラ」「スヵッ」「ヵッォ」などの謎エントリ以外はカタカナとして使っている用例が見当たりません。 単語としての単位ではカタカナからも除外したほうがよく、プログラミング的には漢字として扱って良いのではないかと思います。 余談ながら、カタカナの正規表現では、「ヵヶ」を除外しないでもいい時は [ァ-ヶ] と指定し、除外したい時は [ァ-ヴ] と指定するのが良さそうです。 /\p{scx=Hiragana}/u や /\p{scx=Katakana}/u はこのへんも扱いにくく、非常に使いにくい指定だったりします。 一番致命的なのは「ー」がひらがな/カタカナ判定されたり、記号が含まれるところですけどね。
くり返し記号は他にも「ヽヾゝゞ〃仝々」というものがあります。 先に述べた「々」もこの一部です。 現代語ではほぼ出現しませんが、「いすゞ」のような特殊例があります。 とはいえこちらは基本的には無視して大丈夫でしょう。 他にも「〆〇〻」といった漢字も、稀に出現はします。 ただ指定も面倒臭いので、これらは無視しても大丈夫でしょう。 ほとんどのケースでは、[\u4E00-\u9FFF々]、真面目にやるなら /[\u3400-\u9FFF\uF900-\uFAFF\u{20000}-\u{37FFF}々]/u で大丈夫じゃないでしょうか。
利用用途に応じて範囲を調整しないといけないのが地味に面倒です。

2021年11月2日火曜日

ASMRフォニックスとASMR英単語を作った

英語の聞き流しはリスニング力や定形表現、 アクセント、英単語に効果があると言われているようです。 記憶力が高い子供のほうが良いらしいけど。 たぶん音素の少なさが影響しているんじゃないかな。 英単語を覚える場合なら反復暗記に使えるのが大事と思うので、 聞き流しでチェックできるアプリとして、ASMRフォニックスASMR英単語 を作ってみました。



聞き流しと言っても、完全に聞き流されるとさすがに意味がなさそうなので、 多少の集中力を求める仕組みを用意しました。

最初のうちはスペル付きで発音した後に単語の発音するようにしてみました。 フォニックスを学ぶときに英語の歌を活用している姿をよく見ますが、 それをもっとシンプルにしました。 慣れてきたら同じ英単語を3回しゃべるので、対応する英語を思い浮かべてもらい、日本語で確認します。

ただ聞いているだけだと続かないけど、 ほんの少しでも頭で整理して思い浮かべさせるステップを入れることで、 ダラダラやっていても効果的に学べるようにしたつもりです。 基本は聴くだけですが、もう一度聞く機能などを付けておきました。

完全な聞き流しには案外ならないので、意外と良いかも知れません。

2021年10月26日火曜日

microbundle で custom build

いつもライブラリのカスタムビルドの仕方を忘れるので、メモしておきます。 まず今回カスタムビルドとは <script src="https://hoge.com/fuga.js"> で利用する UMD 形式のライブラリを指します。 最近は <script type="module"> した後に import する ESM 形式も出てきましたが、やはり基本は UMD 形式です。 ESM は UMD を defer して、ちょっと安全にしたようなイメージです。 UMD 形式はレガシーとも言われますが、なんだかんだ使いやすいです。 ただ自分で作ろうとすると、意外とうまく作れない形式でもあります。

そもそもの原因は Webpack というか Node.js です。 ライブラリも大きくなってくると Node.js に起因するパス周りの設定が混沌とするので、そこを Webpack がよしなにやってくれます。 なので UMD 形式のライブラリは、普通なら Webpack を使って作るのが一般的です。 ただ Webpack は事前準備がとにかく面倒なのでなるべく使いたくはありません。 esbuild や swc が UMD 形式をサポートしてくれることに期待していますが、現状ではまだ駄目。 ESM 形式を作りたいなら esbuild --bundle --format=esm fuga.js でいいんですけどね。

結論として、Webpack の設定ファイルがなかったり、自分でカスタマイズしないといけないけど、すぐにビルドしたいような時、今のところ一番簡単そうなのは、microbundle を使う方法です。 以下のコマンドでいけます。
microbundle --name=SugoiPad app.js
UMD ビルドする時にはクラス名 (モジュール名) を付ける必要があるので、 package.json がない場合には --name で名前を付けてあげます。

microbundle を使えば ESM 形式なども同時に作れるので、とても良いです。

2021年10月25日月曜日

jsDelivr の 100MB limit に注意 (Tensorflow.js)

Tensorflow.js 3.10.0 が jsDelivr で死んでいることに気付きました。 jsDelivr はパッケージサイズ (ビルド時のディレクトリ?) が 100MB を超えるとビルドが落ちるようで、 Tensorflow.js 公式でもお決まりの以下の URL は使えなくなっていました。 (追記: 2021-10-28: jsDelivr 側の対応で直ってましたが、100MB の制限自体はあるみたい。)
この問題を誰がどのように直すのかは割と重要な問題ですが、ひとまず動かないと困ります。 すぐに直したい人は自分で Tensorflow.js をビルドしてサーブするか、3.9.0 に fallback するのが良いでしょう。 良い機会だったので最近出てきたカスタムビルド (以下) も試してみました。
カスタムビルド自体は特に迷うところなくできたのですが、あまりサイズは小さくならなかったです…。 しかもまだ動かないっぽいし。 これだったら普通に CDN を使ってもいいかなあ。

最近は jsDelivr はおんぶに抱っこ状態で、他の CDN のことを結構忘れていたので、他のCDN をメモしておきます。 中国にはやたらたくさん CDN があるみたいですが、中国以外の CDN はこの 2つでしょうか。 以下の 2つはビルドもこけてませんでした。 ただ cdnjs は head 指定できないので、更新の激しい Tensorflow.js の指定は面倒そうかなあ。 UNPKG なら Tensorflow.js の head は以下の URL で使えます。 jsDelivr の対応を見て、駄目そうなら今後は Tensorflow.js のサーブはこっちに変更かな。

2021年10月23日土曜日

Deno の外部コマンド実行は zx_deno に全振りが良いかも

Deno は良いところも多いけど、外部コマンド実行はかなり使いにくいです。 たとえば wget https://hoge.com/foo.gz して、gz を解凍する以下のたった 2行の bash コードを Deno スクリプト内で書いてみてください。 簡単なように見えてかなりしんどいです。
wget https://hoge.com/foo.gz -P dir
gzip -d dir/foo.gz
Deno.run() したり、exec ライブラリ を使えば良いというのは誰でも浮かびますし、私も最初はそのように実装しました。 しかし全然うまくいかない。めちゃくちゃ面倒なんですよ! Deno は piped output が 65536 bytes に制限されていたり、 wget や gz のコマンドの終了待ちをしようとしても、コードが長くなったり、うまく待てなかったり、地雷が多いです。 しかもその地雷が触ってみないことにはわからんというのが、一番致命的なところで、ファイルサイズが大きいものを扱おうとすると、途端に死にます。 たかが Linux コマンドを実行するのにこんなに苦労するとは思わなかった。

それならば wget や gzip decompress を Deno 上で実装すればいいのではないか。 確かにそれも選択肢としてアリなんですが、Linux コマンドはよくできているので、そちらのほうがたいてい簡単・省メモリ・高速なんですよね。 そして何よりライブラリを選ぶようなコストを掛けたくない。 やはりコード上で Linux コマンドを自然に叩けることってプログラミングする上で、とても大切だと思うのです。 その点では、Python や Ruby は非常によくできた言語だと再確認できました。

とは言え最近は Deno をよく使っているので、Deno で何とかしたい。 そんな時、少し前に出てきた google/zx、そして Deno ポートの zx_deno があることを思い出しました。 結論だけ言ってしまうと、zx_deno はめちゃくちゃ良いです。 一番最初のコードは以下のように書けます。
import {$} from 'https://deno.land/x/zx_deno/mod.mjs'

await $`wget https://hoge.com/foo.gz -P dir`;
await $`gzip -d dir/foo.gz`;

// process foo
これはいい。今後は zx_deno に全振りで良いんじゃないでしょうか。

2021年10月11日月曜日

すぐに使える棋譜再生アプリ KifuViewer GO を作った

すぐに将棋の棋譜を見ようと思った時、案外困ることが多いです。 アプリを入れれば良いのですが、それが面倒という。 Windows なら良いのですが、Linux は Wine や Mono が必要なので事前準備が多すぎます。 スマホもインストールせず、Web ブラウザ上でサクッと使えると良いなあと思いました。

という訳で、以前作った KifuViewer を応用して、ページにアクセスすればすぐに使える棋譜再生アプリ KifuViewer GO を作りました。





KifuViewer の足回りをきちんとして、サンプルアプリとして公開しました。 以下の機能を加えただけですが、地味に便利。
  • クリップボードから棋譜を読込
  • ファイルを選択して棋譜を読込
  • 棋譜再生をアプリっぽく綺麗に表示

秒速で使えるのがいい感じです。

2021年10月5日火曜日

英語暗算アプリ Calc and Talk と Calc and Type を作った

Calc and TalkCalc and Type を作りました。 四則演算の英文をリスニングして暗算し、答えるゲームです。 Calc and Talk は英語の発音で答え、 Calc and Type は電卓のタイプで答えます。

海外のフォニックス学習でも割と早期から学ぶらしい英文での計算を、一人でできると良いなと思って作ってみました。



私も最初にプレイしてみた時は、やったことがなかったので答えるまでに2秒くらいのラグが出てしまっていました。 これは掛け算で、これは足し算で、と頭の中で反芻するような無駄な思考が入ってしまう状態でした。 しかし問題文は簡単だし、ルールも簡単なので、何回かやってると即答できるようになることがわかりました。

英文を頭に入れて即答する練習にはかなり良い題材かも知れません。

2021年9月21日火曜日

フリゲ紹介: まどか☆マギカ -Try Another Story-

まどか☆マギカ -Try Another Story- は、まどか☆マギカのメインキャラクター、暁美ほむらを主人公とし、 時間遡行からワルプルギス襲来までの一ヶ月を原作を踏襲しつつも異なるストーリーで RPG 化した作品です。 制作 10 年とのことで、ボリュームもクオリティも一級品。 クリアまで約 25時間くらいの長編作品でした。



まどマギを少しでも知っている人なら大いに楽しめる作品です。 時間遡行をくり返す暁美ほむらを操り、今までとは大きく異なる状況で立ち回り、ワルプルギスを乗り切るのが目標となります (下)。



ストーリー重視の RPG で、ストーリーの作り方がとても上手い作品。 最初は魔女の結界を攻略して、グリーフシードを集めながら味方を集めることが目標になります。 非常に多くの魔法少女と出会い、時には戦闘、時には仲間にもなってくれます (下)。 1日が朝昼夕晩に分かれて様々なイベントがあり、時間ごとに出会えるキャラクターが異なります。 イベントも豊富で飽きさせない仕組みが整っていて、また選択肢に応じて進行分岐があり、やり込み要素もあります。 まったく飽きさせることなく、スルスルとストーリーが進むのが、特にすごいと思いました。



魔女結界の探索は、シンボルエンカウントの割とシンプルなものです。 ただギミック満載の結界もあって、探索だけでもかなり楽しめました。 戦闘はサイドビューで、キャラが駆け巡り、非常に凝ったものになっています。 必殺技を放った時のアクションがすごい! こういうアクションを作るのってすごく大変。 しかもキャラクターがたくさんいるので、作るのは本当に大変そう…。



ちなみに作者様の紹介動画はこちら。



序盤・中盤・終盤、あらゆるところに隙がなく、とても面白かったです。 また 1週プレイしただけでも、かなりやり込み要素を感じています。 たとえば、
  • 中盤で唐突に死んでしまった味方を救えるのか?
  • 仲間のページに意外と空白が多い
  • 敵として出てきたままのキャラを救えるのか?
などは気になっています。沼かも知れない。

2021年9月18日土曜日

Tensorflow.js でモデルを学習する

JavaScript (Node.js / Deno / on Browser) でもモデルの学習もやりやすくなってきました。 以前は推論だけならいける状態だったのですが、学習方面まで伸びてきたということです。 今のところ Node.js と on Browser では Tensorflow.js が GPU なしならほぼすべての機能が使えて、 モデルによっては GPU 付きで動く時も出てきて、Deno も WebGPU 付きでそのうち動くかも?みたいな状態です。

私は Node.js より Deno を開発に使う機会が増えてきていますが、深層学習したい場合は Node.js はわかりやすい気がしてます。 何もしてないのに勝手に動かなくなる深層学習では、Node.js のようにバージョンが明確に管理されているほうが良いかも知れません。 ちなみに公式には既にいい感じのサンプルはたくさんあります。

深層学習と言えば Python な昨今ですが、JavaScript で開発する利点は以下と思います。
  • 言語を統一できる
  • 依存性の問題が起きにくい


Python だとすぐ依存性が壊れるので、依存性の問題が起きにくいのは大きいです。 あとフロントエンドに関わる技術をすべて JavaScript に統一できるのはロマンです。 なお以下のような欠点もあります。
  • 情報が少ない
  • Keras や Python の資産を使いにくい
  • on Browser ではモデル保存がしにくい
  • すこし遅い→将来に期待


情報量が少ないのは仕方ありません。 Keras のような気軽に使える仕組みがないのも欠点ですが、Tensorflow が強力なのでだいぶ使いやすくはなってます。 ブラウザだとモデル保存がしにくいので学習は Node.js を使うのが自然です。

巷の実行速度の情報はあまり当てにならないので、いい加減にベンチマークしてみると、real / user / sys は以下のようになりました。 MNIST をありがちな 2層の CNN で、しょぼ CPU で、10 epochs 学習させたときの実行時間です。 上掲の Node.js のサンプルのモデルと epochs を変えただけ。

  • Keras 2.3.1 (Python 3.6.13): 14m / 53m / 53s
  • Tensorflow.js 3.7.0 (Node.js 16.9.1, V8 9.3): 27m / 83m / 120s
  • Tensorflow.js 3.7.0 (Node.js 16.2.0, V8 9.0): 28m / 81m / 155s
  • Tensorflow.js 3.7.0 (Node.js 12.22.6, V8 7.8): 29m / 91m / 34s


Tensorflow.js は Node.js 内部の V8 のバージョン によって速度が変わります。 9.0 から 9.3 までにもかなりパフォーマンスの最適化が入っているので、どうなるかと思いましたが、 思ったよりは変わりませんでした。 結論としてはまだまだですかねえ。 早いと思えば早いし、遅いと思えばやや遅い。 理由としては Node.js では Tensorflow の C++ adapter を呼んでいますが、そこに結構なコストが掛かるからだと思っています。 wasm に変わるとどうなるのでしょうか。 ちなみにこの結果は wasm は効いてないのですが、効くようになればもっと早くなるかも?と書かれています。 なお現状では関数が不足していて動かなかったです。 Node.js での wasm はこのへんに書いてありますが、まだ完璧ではないようです。 最近 ONNX の推論が wasm で動くようになったので、先を越された Tensorflow.js もそのうちサポートするのではないかと思っています。 まあブラウザは WebGL で動くから十分早いんですけどね。

ちなみにいま最も参考になるベンチマーク結果は以下なのかな。 この情報とか、巷のいろいろな情報を総合的に見ると、現状では PyTorch の 3倍遅いくらいになりそう。 wasm が効いた時に 本当に 10倍早くなるなら、一気に JavaScript のほうが早くなりそうですが、どうなるでしょう。 JAX などもあるのでまだまだどうなるかわかりませんが、JavaScript が Python を置き換える時代に期待したいものです。

個人的な認識としては、すべてをサポートしている CPU 版は遅い。 Node.js 版は CPU 版の延長線上で実装されているので、ファイルシステム上の利点は得られるけど、そんなに早くない。 WebGL を使うと一番早いけど、HTTPS 経由の実装になる欠点がある。 ファイルシステム上で効率の良い学習をするには Headless GL などが必要で、そこが詰まっているのでなかなか進まない。 WebGPU も似たような立ち位置にあって、Dawn なるものがあるので期待はできるかも知れないけど、特に何もプランはされていない。 Wasm を使うとすべてに対応できて早そうで期待できるけど、ネイティブ実装よりは遅そうだし、なかなか実装は進んでいない。 そんな感じに見える。

そういや MXNet は使ったことなかったなあ。

2021年9月15日水曜日

発音練習アプリ Pronounce Talk と Pronounce Dojo を作った

Pronounce TalkPronounce Dojo を作りました。 Pronounce Talk は L/R, S/SH などの日本人が苦手な発音を練習するアプリです。 Pronounce Dojo は選択式でそれらの発音を聞き分けるアプリです。

英語教室でやってそうな発音練習を一人でできると良いなと思って作ってみました。 日本人が苦手と言われる頻出英単語はだいたい詰め込んだ気がするけど、他にも見つかったら追加していきます。



Pronounce Talk は十分使いものになるアプリだけど、 好きな子と嫌いな子は明らかに分かれるだろうと思う。 作っておいてアレですが、私なんかは嫌いなほうに分類されます。 speaking より listening と writinig のほうが遥かに日常的に使うので、英語を極めようとする人向けかな。

私は Pronounce Dojo でリスニング問題を聞き分けられれば、それで十分と思ってます。 こちらはまあまあ面白い。 Pronounce Talk は、時間が空いたらちょこっと遊ぶイメージ。 基本的には Speecha で鍛えるのが良いと思います。

2021年9月5日日曜日

小さな子に良さそうなフリゲをまとめてみた

ふと、小さな子どもがフリゲをする時に何をおすすめすれば良いんだろうと思いました。 ちょっと気を引くくらいなら、派手ではなくて、当たり障りなく、漢字が少なく、短編で、難しいルールの少ない作品が良いです。 光の刺激が強いものはてんかん的症状や、ゲーム依存性に繋がることもある、とも言われているので、派手なゲームではないほうが良いと思っています。 細かい解説なども理解しきれないことを想定して、なんとなくわかるものが良いと思う。 漢字が OK ならもっと色々ありますが、漢字のしばりを入れると、意外とおすすめしやすいものは少ない。

おすすめできそうだったのは以下の作品。 教育的によろしいものと考えると、やはりパズルゲームがおすすめしやすそうですね。 小さな子だとあっさりのほうが良いと思う。 あっさりという意味では ABA Games さんの作品が良い気も。

RPG

  • 影明かし
  • 武神の目覚め
  • ふしぎの城のヘレン
  • ぼくらの大革命!
  • 前向き勇者
  • ダーリン菊一文字
  • ネフェシエル

ACT

  • ミクものがたり ~ユメとたいせつなもの~
  • Sacrifice -サクリファイス-
  • ぽんこつポン子の小冒険
  • ぶきあつめ ~なんでも武器になるRPG~
  • ロストアルター
  • 真夏の島の宝船
  • 妖精とゴーレム
  • Clean Out The House
  • 斬るビル
  • 納豆
  • いかちゃん
  • モノリスフィア
  • Wind I Upgrade
  • Trial Windy

パズル

  • ネコトパズル
  • 雨宿りのパズル
  • スターダスト ディフェンス -Extension-
  • REC
  • ボムっと発掘!とれじゃーはんたぁ
  • デュラハンさんの頭をお持ち帰りする
  • 氷水火山SOKOBAN (すこし難しいかな)
  • Stick Robot
  • BLOCKSUM
  • Hopeful
  • 銀竜奥義伝
  • ねずみてくてく
  • 常闇を翔る一筋の閃光
  • 魔法使いの倉庫
  • サーモンの鍵
  • みすてぃっく☆ばる〜ん (Circus は難しい)

アクションパズル

  • LittleFrame
  • Win' Wind' Windy
  • しろくろシンドローム
  • 算盤の鍵 (難しい)
  • Windy Possetion (難しい)

シューティング

  • DAY:0
  • Noiz2sa
  • GENETOS
  • クゥるべーだー
  • Trial Windy
  • Super Depth

その他

  • マリィと賢者の森
  • 上原テトリス
  • とつげき!ダンジョン
  • QWOP
  • 出過杉くん

パッと思い付いたものを書き留めただけなので、他にも良いものはあると思う。 そういえば私が最初に遊んだゲームは、すごいシンプルなシューティングでした。 今ってシンプルなシューティングはあんまないですね。

2021年9月2日木曜日

フォニックス×手書き練習アプリ Tegaki Phonics を作った

Tegaki Phonics を作りました。 小学生のための英単語アプリです。

小学校の英語は学習内容がきちんと決まってないので、 適当な学校と真面目な学校で格差が広がります。 特に英単語をどれだけ覚えているかは大きな差になりそうです。 そこをなんとかするための英単語アプリです。



小学生が覚えやすい英単語とは?

これまでにも効率的な英単語アプリとして Vocabee を作ってきました。 Vocabee は英単語を覚えることに慣れており、中学生以上の文法を考慮した学習には最適ですが、英単語をおぼえたことのない小学生にはつらい部分もありそうです。 文法を考慮しない基礎力としての英単語を、もう少しきちんとチェックしたいと思いました。 これは book とか fox みたいな統計から外れた超簡単な英単語を指します。

そんな時、どんな英単語をチェックすれば効率が良さそうかという話があります。 小学生用の参考書をいろいろ見てみたのですが、本当に効率が良いかは少し怪しいところがあります。 大人が見ても「へー知らなかったわ」みたいな単語、しかも覚えにくいものがたまーに入っていたりします。 思ったのですが、小学生はフォニックスの英単語でチェックするのがわかりやすそうです。 フォニックスの英単語は 20こぐらいずつに区切られている上に、音素が少なく、意味もわかりやすく、 これ以上覚えやすいものはないと思います。 ということで、フォニックスの英単語をまとめた英単語アプリを作りました。

作ってみた

フォニックスの英単語を四択問題でおぼえるのはあまり良い方法に思えません。 意味がわかりやすいなら画像を付けるべきだし、文字数が少ないなら書いて覚えるのも有効です。 ということで、手書きで覚えるようにしてみました。

発音が決まってる=一部のアルファベットは固定される=日本語から英語の変換が重視されるということで、 通常の英単語のような出題形式でテストするのが良さそうです。 EASY モードはおぼえる専用ですが、HARD モードではテストもできます。

2021年9月1日水曜日

フォニックスタイピングを作った

フォニックスタイピング を作りました。



海外のフォニックス事情

作るに当たっては、フォニックスについて割といろいろ考えました。 海外で英語を習うとき、5-7歳 (幼稚園から小2) で まずフォニックスを習って、並行してサイトワーズを習います。 その時に習うのは ABC くらいで、つまり 英単語の ABC が読めないけど、会話ができる状態とのことです。 このような順序になるのは、アルファベットが 26 種類しかないのに対し、フォニックスなどの発音パターンは100種類近くあり、覚えるのが大変だから。 漢字は一つ一つ順番に覚えればスムーズに覚えられるけど、 英単語は実のところ漢字より前提ルールが多いのは、注目ポイントのように思います。 このような事情があり音から入るため、海外では文字を書いて理解するまでにはかなり時間が掛かる。 たまに海外では文字を書くのはいい加減でいいのに日本ではなぜみたいな記事を見かけるのですが、日本とは学習価値が異なるからで、話半分に聞くべきな気がする。

フォニックスはアメリカとイギリスで発音が違ったり、例外も多いので、 海外では音が同じものだけまとめて、ざっくり学ぶことが多いみたいです。 ただ勉強の時間軸はかなり長くて、3年くらい徹底的にやるという記事を多く見かけます。 では日本でもそうすべきかと言うと、前提が違いすぎるよね。 海外は日常的に英語で話していていくらでも音が聞けるけど、日本は聞けない。 真似しようとするのは大変だし、本当に真似が必要なのかも怪しい。 だいたい日本人は発音より英単語の文字列を眺める機会のほうが圧倒的に多いのですから、 音素から学ぼうとしてもデッドロックになるだけです (既になってる?)。 もう少し良い学びはないものかとは前から思っています。

日本人向けのフォニックス

そんなこともあって最近はフォニックスの色々な教材を見ていました。 発音練習についてはやはり面倒くささを感じるのですが、学び方自体はとても参考になります。 そこでフォニックスの考え方だけを流用しながら、 日本人向けにアレンジしたおきらく学習アプリを考えてみました。

フォニックスでは多少の効率は無視して、 なるべくアルファベット / 音素の少ない単語で、覚えやすさを重視しています。 音素に区切るのと一回あたりに学ぶ文字量・単語量が少なくなるので、英単語チェックもしやすいです。 このへんの仕組みは子供にとってユーザーフレンドリーで、真似をしたいと思いました。

日本人にとって ABC はひらがなより簡単なので、 ほとんどの子は音と ABC の対応を一瞬で覚えられると思っています。 しかし単語になると、昔の学習法だと配列の手掛かりを知る学習が少ないため、結構なハードルがあります。 なのでその部分だけフォニックスでサックリ学び、 後はタイピングなどの英語ゲームで物量をこなせば、 勝手に音素の決まりが定着し、いい感じに英単語へ繋がっていく気がします。 あとは量をやるだけ。

作ってみた

ということで、ABC → フォニックス → 英単語のスムーズな学びができるように、 フォニックスの音素で区切って物量をこなすタイピングを作ってみました。 タイピングは反復的に作業するので割と相性がいい。 エンドレスにすると単語の種類が少なくマンネリしそうなので、 15 の単語をできるだけ高速に打ち込むゲームにしてみました。 10問だとクリアが簡単そうだし、20 は少し長そうなので、今のところ 15 が良さそう。 15 * 3〜6 = 60〜80文字くらいなので、小さな子感覚では EASY を 30〜40秒くらいでクリアすればだいたい暗記はできている。 HARD を 60秒以内にクリアできれば、十分理解していると言って良いのではないかな。 タイピングと組み合わせるのが良いのは、すぐに単語が出てくるまで理解しているかどうかわかるから。 速度が遅くなれば理解度が足りないことがすぐわかる。 またヒントを連発していると最大 75秒遅れるので、60秒以内のクリアはできない。

もう少し面白くしたり、画像などもほしいですが、まずは動くものを出すことを優先して作りました。 フォニックスタイピング だけでも 400単語くらいは覚えられるので、英単語タイピング で少しいい加減だった小1〜小3の穴は埋められた気がします。

ちなみにフォニックスのルールは別の書籍やサイトで軽く学んだ後、タイピングで物量を流して覚える。 そして単語の意味を覚えているかどうかチェックするような利用用途を想定しています。 ルール解説はたくさん良いサイトがあるので、作らなくても良いかな。

フォニックスの発音練習アプリ Talk Phonics を作った

Talk Phonics を作りました。 名前の通りフォニックスの発音練習ができるゲームです。



最近の学校とか、英語教室とか、発音練習に熱心なところもありますね。 発音練習はネイティブの人と一対一が多いけど、そのせいで完全にデッドロックになってる気がしています。 でも最近の AI は強いので、発音練習くらいなら AI 任せでどうにでもなる時代です。 しかも一度作ってしまえば AI の精度向上は Google や Apple 任せにできる。 今回作った Talk Phonics はフォニックスを一人で練習できる、とってもエコなアプリです。

英会話クイズ とまったく同じ仕組みで動いていますが、 小さな子向けに 15こだけ練習すれば良いようにしてみました。 採点は割ときびしいです。 人間の採点では「まあいっか」で済ましているところがあると思いますが、相手は AI なので、きびしく駄目出ししてくれます。 時には採点がきびしくてツライ時もあるのですが、文脈を考慮して採点がゆるくなります。 ツライ時は文章で答えてみてください。 他にもスキップできるようにしてあったり、救済措置はいろいろ入れてます。

2021年8月29日日曜日

Tanaka Corpus をオレオレ改良した

日英対訳コーパスの Tanaka Corpus を改良したものを GitHub で配布し始めました。 だいぶ前に作って「誰も使わんしええやろ」と思って記事にし忘れていたんですが、記事にしておいたほうが良い情勢になってきたので、書いておきます。

tanaka-corpus-plus

まず機械翻訳や英語アプリを作るにあたって、対訳コーパスは大切です。 今なら C4 がデータセットとして一番有名ですが、 私のような一般人には「 HDD に入らないっす」となりがちです。 そこで中規模の使いやすい日英対訳コーパスとして、Tanaka Corpus を使うのは良い選択肢です。

しかし Tanaka Corpus は結構扱いに困るところもあります。 まず 1年前はあまり更新されない静的な形式のデータがダウンロードできたのですが、 一時期ダウンロードできない時期があって、 その後気付いたらデータが自動更新されるようになっていました。 データは Tatoeba と同期しているのだと理解しています。 Tatoeba は Tanaka Corpus のデータセットを引き継ぎ、多言語化したデータセットです。 じゃあ Tatoeba や自動更新されるデータを使えば良いんじゃないか? そうでもないです。

もともとの Tanaka Corpus はややノイズがあるものの、大学生の方が頑張って作ったものです。 しかし Tatoeba は自由にユーザ投稿できるコーパスで、データチェックはしていません (と理解しています)。 そのためバージョン付きの配布形式でメンテしないと用途によっては扱いに困る部分があるような気がしました。 そこで古いデータを保存し、ベースにした上で、Tanaka Corpus を自分でノイズ除去をしようと思いました。 しかし何の目的もなくメンテするのはつらいので、いくつかの英語ゲーム (下) を作って、 遊びながらノイズのある翻訳を削除しています。



遊んでいたら割と気楽にノイズが削れたので、 ノイズを除去した日英対訳コーパスを配布することにしました。 ちなみに明らかにノイズなものは、手間を考えて再翻訳ではなく削除してます。 名前を保持したほうがわかりやすいと思い、安直に Tanaka Corpus Plus としました。 改変内容を確認しながら共有したほうが良いと思うので、そこをきちんと残しつつ CC-BY で配布しています。 データが欲しい人はここからどうぞ。 もちろん大学生の方が頑張って作ったオリジナルのものも含まれています。

フリゲ紹介: 銀竜奥義伝

今年も ウディコン受賞作品 を上位 20 件+α をプレイしてみました。 ランキングが出る前から色々プレイしていたのですが、 どれも手堅く同じくらい面白かったので、そんな中でどういった内容が好まれるのかは、気になっていました。 ゲームを作る人にとっても参考になる情報が詰まったコンテストだったような気がします。

たくさんプレイして私が真っ先に思ったのは、 適度な難易度で遊びやすく、シンプルかつコンパクトにまとまっていると、印象に残りやすく良いということです。 銀竜奥義伝独立理想国家アヴァロン終末の案内人ゴルガン が上位にくる予感はありました。 他にも私はパズルが割と好きなので、 ALICE IN ANOTHER WORLD -鏡の国のアリス-HopefulW・ワドルーの冒険雨宿りのパズル などを真っ先にプレイしました。 どれも面白いと思いましたが、合計となると物語性に加点が難しかったかな。 上位に食い込むのは難しかったようですが、シンプルなので中堅に多く入っていたのが印象的でした。

ECO2黄昏の時に巡り会うもの などもランキングが出る前にプレイしていて面白かったのですが、総合順位で見ると圏外でした。 ランキング上位に入るのは本当に大変ですね。 技術的には非常に高くても順位の伸びきらなかった作品も多かった気がします。 これはシステムの複雑さの排除や、適切な難易度調整などが、昨今はとても重要ということを意味しているのだと思います。 ウディコン審査期間終了後、ふりーむのDL数を見ていると、また少し違った印象も受けます。 実況のしやすさもそれなりに影響がありそうなのと、ホラーや尖った作品、ノンフィールド RPG が強い印象です。 様々な作品の評価をよく見ていくと、ユーザが求めていることが色々とわかり、とても参考になりました。 もっともどの作品も面白く、気にしすぎる必要はないと思います。



さて今年一番は、私も 銀竜奥義伝 と思いました。

地元の格闘大会で優勝した少女メイメイが、同年代のリンに敗れたことにより、 銀竜谷にある超闘技場への挑戦をすることになりました。 銀竜奥義伝 は超闘技場で優勝するまでを描いた物語です (下)。 クリアまで 2時間くらいで、適度な難易度で遊びやすく、シンプルかつコンパクトにまとまっている上、 グラフィック、チュートリアル、動作の軽さなど、基本部分が突出していたのが、高評価に繋がった気がしています。 昨年は Sebastian and Little lady Butler of Love で上位受賞されている作者様で、 昨年もグラフィックが凄かったですが、今年もすごい。 企業のゲームと言ってもまったく遜色ないです。 ADV から SRPG にギアチェンジして出してくるのも芸風が広いなあと思いました。



SRPG といってもステージは割とあっさりで (左下)、育成型のパズルに近いのではないかと思いました。 高得点を狙おうとすると「どのステータスをどこまで上げて、どの必殺技を覚えて、どのように動けば高得点で勝てる」かを考える必要がありそうです (右下)。 実は魔法の塔とかなり近い部類のパズルゲームなのではないかと私は思いました。 モードを選べるようにしてノーマルモードの難易度を大きく抑える代わりに、 量を抑えてやりこみ要素+ランキングを用意したのは、コンテストを活かして戦略的だったかも知れません。



私はノーマルモードでさっくりクリアしたのですが、ストーリーのほうもすっきり感があって良かったです。 前作と同様、コメディ+あっさりお色気 (前作より控えめ) を入れて、飽きにくい内容になっていたのが印象的でした。 昨今のライトゲームの作り方として、学ぶことの多い作品だったように思います。 2時間とあっさりめの作品なので、ハードモードからが本番かもしれません。

2021年8月14日土曜日

GitHub の personal access token で苦労した

今日から GitHub の personal access token の設定が必要になり、下のエラーが出るようになりました。 これ対応に苦労したのは私だけなんだろうか。
remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.
remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information.
fatal: unable to access 'https://github.com/[user]/[repo]/': The requested URL returned error: 403


ちなみにリポジトリごとの remote url を以下のように変えれば動くのはわかってます。 ただこの方法だとリポジトリが死ぬほどたくさんあると、頻繁に書き換えないといけないので死にます。 Grep & Replace で対応してるといつかやらかしそうなので、もっと簡単に一括管理できる方法が必要。
# from
https://github.com/[user]/[repo]
# to
https://[user]:[token]@github.com/[user]/[repo]


最初は credential 設定だけでいけるんじゃないかと思いました。でも駄目。
# from command
git config --global credential.helper store
# or edit ~/.gitconfig
[credential]
  helper = store
上記を設定した後に、参照先である ~/.git-credentials に URL設定 (下) を記載すればいけるやろと思ったら、変わらずアクセストークン必要と怒られて困りました。 Windows だとイケるよみたいなサイトがたくさんあるんですが、Linux でまったく動かないのは何なのだろう。 自分の公開リポジトリに対してチェックしているので少し状況は違うかも知れないけど、とりあえず駄目。
https://[user]:[token]@github.com


次に、URL を書き換えればいけるかなと思ったのですが、これも駄目でした。
# from command
git config --global \
  url."https://github.com/".insteadOf \
  https://[user]:[token]@github.com/
# or edit ~/.gitconfig
[url "https://github.com/"]
  insteadOf = https://[user]:[token]@github.com/
怪しげなテクは許してくれないみたいです。


解決策がまったくわからず、仕方ないので SSH 経由でアクセスしたらいけました。
# from command
git remote set-url origin git@github.com[user]/[repo].git
# or edit ~/.gitconfig
[remote "origin"]
  url = git@github.com:[user]/[repo].git
まあ SSH 経由ならトークンいらないと書いてあるのでそりゃそうなのですが、 HTTPS 経由の根本的な解決にはなってない気がする。 まあリポジトリの URL を書き換えさえすればいいので、楽と言えば楽です。

うーん、公式ドキュメントはちょっと説明不足かも? それとも何か設定を見落としているのかな。

2021年8月13日金曜日

フリゲ紹介: 氷水火山SOKOBAN

氷水火山SOKOBAN は、VIPRPG 2021夏の陣 に投稿されていた倉庫番ゲーム。 ただし普通の倉庫番ではなく、魔法具現化たちの力を活かしたパズルゲームになっています。 あまり正確に測ってませんが、一応のクリアでも 4-5時間くらい掛かったかも知れません。 かなり頭を使うパズルゲームです。



主人公のアイスⅢが水氷系の具現化のサポートを得ながら、キャラ切り替えを駆使してクリアを目指します。 階段にたどり着けばクリアで、まんま倉庫番のステージもあれば、よりパズル的なステージもあります。 ステージをクリアするにつれルールがじょじょに増えていき、操作できない味方なども出てきます (左下)。

火系の具現化が敵として登場し、触れると味方は解けます。 例えばアイスⅢがファイアⅠなどに触れるとアイスⅡとウォーターⅠに分裂します。 ウォーターⅠがファイアⅠに触れると消滅してしまいます。 アイス系は柵を越えられない一方で排水溝の上を歩けますが、 ウォーター系は柵を通り抜けられる一方で排水溝を滑り落ちて消滅してしまいます。

他にもいくつかルールはありますが、直感的なルールでわかりやすく、 またパズルを解いていくたびに少しずつ説明してくれるのでわかりやすい。 UI も何度でもやり直しがしやすく作られていて、リトライ性が高くて良かったです。 よく練られたパズルゲームで非常に面白いです。

パズルゲームにしては珍しいボス戦も (右下)。 ボス戦では上下左右からボスを同時攻撃すると倒すことができます。 分裂を活かしながら戦っていくのが新鮮で面白かった。



全体的にとても完成度が高いですが、序盤から時たま難しい問題が混じっています。 たとえば私は No.18 がいまだに解けてません。 No.7 とかも最初は飛ばしてました。 それ以外は No.27 くらいまで割とサクサク解けているので、 苦手な問題があったらスキップ機能を使用してプレイしても良いかも知れません。

No.28 以降は状態変化しやすくなり、扉も増え、さらに厄介なルールも追加されてかなり歯ごたえがありますが、 ここでもスキップ機能を連打してしまえばクリア自体は簡単になってます。 またチート機能として Ctrl キーを押すとどこでも歩けるので、他にもいろいろな救済措置 (?) があります。

合間時間に少しずつプレイしやすい作品で、とても面白かったです。 こういった具現化系のパズルってどれも面白いですよね。

2021年8月7日土曜日

ABC の手書き練習アプリ Tegaki ABC を作った

以前作った手書きのアルファベット認識がバグっていたようで、 直してみたら MNIST より少し低いくらいの精度が得られました。 お手製のデータだから比較のしようがないけど、 EMNIST のアルファベットよりずっと実用的で、データ量も結構あるので、 クラス数を考えれば十二分に使い物になると思う。

精度は肌で感じるくらい高くなっていて、誤認識はほぼしなくなりました。かなりいい感じ。 せっかく精度が上がったので、新たに Tegaki ABC を作りました。 手書きで ABC をテストするゲームです。

シンプル過ぎてあまり説明することもないアプリだけど、 最近の子は文字を書いてないことが多いので、最初の練習用になかなか良いと思う。



以前アルファベット認識のサンプルアプリとして作った 英単語クイズ も、 かなり改良を加えた上で直しておきました。

2021年8月2日月曜日

英会話アプリ Speecha を作った

ネコでもできる英会話アプリ Speecha を作りました。 なお簡単とは言ってない。 マスコットがネコというだけだったりします。



英会話アプリって、英文と文章正規化アルゴリズムを用意すれば、実は作れてしまいます。 ササッと作ってみたら案外うまくできました。 英会話というよりはセンテンスリピーティングですが、練習としては十二分に価値があります。 英会話は高いし、そもそも英会話をするより前に、短文くらいはサクサク話せるようになっておいたほうが良いはずです。 Speecha はそんな時の練習アプリとしてはなかなか良い出来だと思う。

認識精度はかなり良いです。 下手くそな発音だと駄目出ししてくれるのも良い感じで、練習になります。 ただ発音が難しいっぽいものが少しあって、たぶん日本の人名とか地名の判定が難しいです。 英語に最適化されているので仕方ないかな。 やたら難しいのがあったらスキップできるようにしておきました。

ちょっとした練習アプリとしてはなかなかいい感じ。 低学年の例文はもう少し改善できそうな気がするので、今後も改良してみたい。 作ってみて思うけど、やっぱ発音練習って難しいわ。 一文を練習するのにすごく時間が掛かるので、しゃべるのが苦手な子はやらなくていいと思う。 英会話の前に国語と算数ができないと話にならないし、 英会話をやるより前にやるべきことがたくさんあるのではと、私は思いました。 あくまで得意な人がさらに得意になるための道具。

作ってみて改めて思うけど、最近流行りの話して覚える英語はかなり人を選ぶと思います。 一方でなんとなく覚える英語教育はすごく良いと思ってて、そのためのツールを色々遊びで作ってみてます。 話すのが面倒な人はタイピングとかでガーッと覚えるほうが気持ちも楽でいいんじゃないかな。

2021年8月1日日曜日

あいうえおタイピングを作った

あいうえおタイピング を作りました。 以前作った ABCタイピング で ABC をマスターした後に、あいうえおをマスターするアプリです。 このアプリで慣れれば、巷のタイピングゲームはいくらでも遊べるようになるはずです。



ABCタイピング は最初にやるタイピングとして最良のアプリと思っているのですが、その次にやるものが難しいです。 巷のタイピングゲームは小さな子にとってギャップが大き過ぎて、先に「あいうえお」の入力の慣れないと、なかなか大変です。 そこで小さな子でもタイピングを飽きずに学べる あいうえおタイピング を作りました。

コンセプトは ABCタイピング と同様に、説明ゼロの UI です。 そもそも、あいうえおの配列を一つ一つ説明するのって面倒だし、子供も退屈と思います。 そこで一切の説明をなくして時間内にあいうえおをひたすら打ち込むアプリにしました。 シンプルですが、1分単位で自分の成長を見れるので、飽きやすい小さな子には遊びやすいと思ってます。

普通のタイピングゲームはだいたい 2分単位でゲームをやりますが、小1くらいの小さな子に 2分は長いんですよね。 その点、ABC は小さな子でも慣れれば 15秒、あいうえおは 50秒くらいで入力できます。 飽きる前に自分の成長が見れるため、これくらいシンプルなほうが受けがいいのではと思いました。 あ~んの入力中に集中力が切れてしまう子もいると思ったので、25秒くらいで練習できるモードも作っておきました。

2021年7月26日月曜日

世界の塾と学校

中国では学習塾と宿題が禁止になるらしいです。 日本とは比較にならない競争社会のため、少子化を懸念してのことらしい。

中国や韓国、シンガポールの塾事情が熾烈であることは有名ですね。 日本とは比較にもならない。 小学生低学年から夜10時まで勉強と言われていて、さすがにやり過ぎ感はあり、それが良いこととは思いません。 多少の是正にはなるのではないでしょうか。 ただ正直びっくりですよね。

良い機会なので海外の塾や学校の事情に目を向けてみましょう。 ちなみに世界の塾事情は、以下のページが割と詳しいです。 より詳しくはネットで検索してみると、色々な情報が得られます。

ヨーロッパ: 選別型

ヨーロッパは何度もやり直しができる国と、バッサリ選別される国があります。 ヨーロッパに塾や予備校はほとんどありませんが、幼少期から進路を選別される国では、補習塾のようなものはあるらしい。 このページではドイツを例に挙げていますが、結構同じスタイルの国は多いです。 何度もやり直しができる国も、デンマークのように入職年齢を下げる狙いで一部是正を始めている国があります。

ヨーロッパ: 何度でもやり直せる型

イギリスや北欧などでは、いくらでも先取りできますが、いくらでも遅れることもでき、そして何度でもやり直しができます。 聞こえはいいですが、小1から留年が当たり前にあるので、しないように自立学習が必要になってきます。 結果だけみれば職業が安定するのが30歳以上のことが多く、問題は多いです。 自由と自立に重きを置いているのはわかりますが、塾がないことが本当に良いことかはわからないところがあります。

塾はほとんどないと聞きます。 しかしイギリスの富裕層では大英帝国の歴史などを汲んで家庭教師の比率もそれなりにあるため、塾のような存在がまったくない訳ではありません。 塾のほうが家庭教師よりずっと効率良いから、単純に歴史の差ではないかな。

アメリカ: 何度でもやり直せる型 + GPA

アメリカに塾も予備校もほとんどないのは、大学に入るための一発入試がないからと言われてます。 それ自体は確かに良い事かも知れない。 ただ GPA に毒されているのが必ずしも良いものかはわかりません。 なぜなら突然やる気になっても、GPA で判断されれば、それを認められないことになるからです。 また名門大学に行くためには名門私立学校に入るための競争があり、学費はめちゃくちゃ高くなるし、より激しい競争がある。 なんか日本の私立の感覚に近い気がします。

インド:東アジア以上に苛烈?

インドってどうなんだろうと思って調べてみると、3歳までのプレスクールにおける教育なども重視されており、 また小学生になるより前に算数を習っています。 カースト制度から逃れるために IT 教育も他国とは比較にならないほど進んでいるので、塾がどうこう以前の問題として、東アジア以上に苛烈そうです。 また発言や発表を大切にした教育など、欧米に近い教育が重視されているのも注目すべきポイントかも知れません。

東アジアは全体的に熱心

台湾は IT化で一時期より減ったと言われていますが、大学進学率が非常に高いので勉強は非常に熱心です。 タイは日本からかなり塾が進出してますが、どれくらい激しいかはよくわかりません。 フィリピンは英語大国なので、塾以前の話として英語教育がすごく熱心です。 ベトナムも英語教室が人気とか。 インドネシアやマレーシアも日本から結構進出しているので、盛んそうな印象はありますが、実情はよくわかりません。

その他:不明

中南米や中東だとどうなっているんだろうと思いましたが、情報は見つからず。 オーストラリアは補習塾や進学塾が一応あるみたいです。

まとめ

結局のところ、塾があろうとなかろうと競争はどの国にも存在します。 中国で塾がなくなったとしても競争は残ります。 ニュースでいうところの塾が、筆記対策のことを意味しているなら、GPA 重視のアメリカ的な社会になるのかも知れません。 実際のところはもう少し経ってみないとわからなそうですが、詰め込み型から思考力への抜本的な改革と考えることもできそうです。

日本なんかも関心・意欲・態度みたいな謎項目が追加されているので、GPA 型に分類される気はします。 ただ評価基準が謎で、昔も似たようなものはあったけど、ここまで酷くはなかったと思う。 学問の学び方は小中学校に閉じているものではないので、関心・意欲・態度のような尺度に何か意味があるのかなって思いますね。 GPA 型の欠点は、GPA を取ることだけに集中して、本質的な学びにならなくなってしまうことです。

しかし最近の中国はどこへ向かおうとしているのでしょうね。 香港、国内巨大IT規制強化、総量規制、仮想通貨の規制強化、塾の撤廃、ゲーム敵対視、意図的なクラッシュ。これらは何を意味するのでしょうか。 完璧な国家管理主義をどんどん目指しているように見えます。

そういえば中国には昔、科挙がありました。 いつなくなったのかと言えば、なんと明治の 1904年だったらしいです。 1895年の日清戦争に負けて見直したということになります。 こういった過去もあって、教育のドラスティックな改革に躊躇がなく、また実際に成果を出してきたからとも言えそうです。 下のリンクに書いてあることはとても参考になります。 科挙に通った人材は「暗記能力だけ」「受験生の資金不足」「試験問題が時代遅れ」「科挙に合格した官僚は数字にめっぽう弱い」とのことです。 また「実務能力に乏しく、科学技術的な知識を軽視して、文科的な知識を重視するので、科学技術が振興されない」などは、 形は若干形は違えども今の日本が直面している問題と同質のものに思えます。



ところで、Twitter でこんなものを見つけました。



中国はめちゃくちゃ投資してるので、これもその一貫となるのかどうか。

その他の関連報道:

2021年7月22日木曜日

ABCタイピングを作った

ABCタイピングを作りました。



アルファベットも初めての子が ABC の説明なしにタイピングをできるように作ってみました。 アルファベットを見たことがない子からすると、ABC を打つタイピングって、 形と音とシーケンスでおぼえてポチポチするゲームという認識だと思います。 実際いくつかそういうゲームはあるのですが、使い勝手が良くないので、作ってみました。

あとでもう少し面白くするつもり。

2021年7月19日月曜日

JavaScript の keydown は OS で動作が異なる

JavaScript の keydown は OS で動作が異なることに気付きました。 昔は色々と動作が違っていたのは知っているのですが、最近はもう大丈夫だろうと思い込んでました。

1. イベントの発生状態の違い

ほとんどの OS では、例えば Shift キーなどを押しっぱなしにしているとき、keydown イベントが呼ばれ続けます。 でも Linux だけは、最初に押された時しか呼ばれません。 Linux では Chrome も Firefox も最初に押された時しか反応しないので、 どうやらこれは OS の動作の違いなのだとわかりました。

2. キーボード配列の考慮レベルの違い

keydown で取得できる英語の記号文字は、Firefox では使用しているキーボードの配列に準拠したものが取得できます。 しかし Chrome では Linux / Android で英語キーボードのものになってしまいます。 たとえば日本語キーボードで「"」を押しても「@」が返却されます。 イベントの発生状態はまだしも、これはめちゃくちゃ困るのでバグみを感じます。

タイピングゲームを作ってると地味に影響あります。 最近は気になることはだいぶ減りましたが、まだまだ完璧な互換性はないんだなあ。 そして情報がない。

2021年7月14日水曜日

Denoでよく使う書き方まとめ

そろそろ Deno をメインにしても良いんじゃないかと思って使い始めてみてますが、あまり使い方がまとまってないので、自分用にまとめてみました。 これだけわかれば、普通の言語並に使いやすいんじゃないかな。 よく使うとこだけのまとめなので、困った時は公式のリファレンスを見ましょう。 Node.js でよく使う書き方との対比付きです。



sprintf → 標準ライブラリで可能

import { printf, sprintf } from "https://deno.land/std/fmt/printf.ts";
printf("hello %s", "world");
console.log(sprintf("hello %s", "world"));

printf (改行なし) → 少し面倒

REPL 上だとうまく動かないのが難点…。 このコードを見てもわかるように、Deno だと TextEncoder が至るところで登場してきます。
 await Deno.stdout.write(new TextEncoder().encode("hello: "));

コマンドライン引数 → Node.js より簡潔

Node だと配列のインデックスが 2 から始まるのでわかりにくいけど、Deno だと 0 から始まるため直感的でいい。
if (Deno.args.length < 3) {
  console.log("USAGE: test.js [file]");
  Deno.exit(1);
}
for (let i = 0; i < Deno.args.length; i++){
  console.log(Deno.args[i]);
}

sleep → 外部モジュールで OK

Node.js で真っ先にハマるのは sleep でしょう。 たかが sleep のために npm install はしたくないのが Node.js ですが、書き方は色々あるし事故りやすいです。 しかし Deno は外部モジュールを気楽に使えます。 たとえば以下で良いと思う。楽ちん。
import { sleep } from "https://deno.land/x/sleep/mod.ts";
await sleep(5);

ファイル読み込み (一度に全部) → Node.js より少し楽

const text = Deno.readTextFile("input.txt").then(response => {
  console.log(response);
});

ファイル読み込み (複数を同期的に1行ずつ) → Node.js より超楽

readLines が用意されてたので超簡単になりました。 しかし Node.js と同様に終端で empty string を返してくるのが厄介。 個人的バグの踏みやすさ No.1 は間違いなくこれ。 そんな仕様は Node.js と Deno くらいでいつも疑問なのだけど、undefined チェックするしかないのかな。
import { readLines } from "https://deno.land/std/io/mod.ts";

for (let i=0; i<3; i++) {
  const fileReader = await Deno.open(i + ".txt");
  for await (const line of readLines(fileReader)) {
    if (!line) continue;
    console.log(line);
  }
}
今のところ 1行あたりの文字数が多いと動かない問題を確認している。 そんな時は readLines ではなく readStringDelim を使わないといけない (仕様?)。 readLines も readStringDelim を呼び出しているだけなので、何らかの limit に引っ掛かっているみたいだけど、よくわからない。 サイズが大きくなった時は、いろいろ注意しながら作らないと予期せぬバグが発生しやすい。
import { readStringDelim } from "https://deno.land/std/io/mod.ts";

const fileReader = await Deno.open("long-line.txt");
for await (const line of readStringDelim(fileReader, "\n")) {
  if (!line) continue;
  console.log(line);
}

encoding → あまり想定されてない?

Shift JIS のファイル読み込みなどは、いまのところできないように見えます。 パッと見では想定されてないように見えます。 Windows が面倒そう。 一応ライブラリを使えばいけるみたい (下)。

ファイル書き込み

Deno.writeTextFile("./hello.txt", "Hello World!");

ファイルとディレクトリの判別

関数が変数になったり、細かなところが少し変わった。
const stat = await Deno.stat("/dir/path");
if (stat.isDirectory) {
  console.log("This is a directory");
}

glob → 標準ライブラリで可能に

import { expandGlob } from "https://deno.land/std/fs/expand_glob.ts";

for await (const file of expandGlob("**/*.txt", { globstar: true })) {
  console.log(file);
}

時間計測 → Node.js と一緒

console.time("hoge");
// 処理
console.timeEnd("hoge");

シェル実行 → Node.js のほうが楽

TextDecoder が邪魔で、Node.js のほうが楽な件。 cmd でファイルを渡す時にワイルドカードが効かないのは面倒だなあ。 ワイルドカードなどは bash とかの機能だから、起動が速いなどの利点があるのかな。 ただ常用するには辛いものがあるので、bash -c での実行か、glob を利用した実行を想定しているのかも知れない。
// とりあえず実行したいとき
const p = Deno.run({ cmd: ["cp", "a", "b"] });
// 実行結果が欲しいとき
const p = Deno.run({
  cmd: ["ls', "-altr"],
  // cmd: ["ls", "-altr", "*"],  // これが動かないのはつらい
  // cmd: ["bash", "-c", "ls -altr *"],  // これなら動く
  cwd: "working-directory",
  stdout: "piped",
  stderr: "piped",
  stdin: "null" });
let [status, stdout, stderr] = await Promise.all([
  p.status(), p.output(), p.stderrOutput(),
]);
const textDecoder = new TextDecoder();
stdout = textDecoder.decode(stdout);
stderr = textDecoder.decode(stderr);
p.close();

面倒なので、exec ライブラリが今のところ人気みたいです。
import { exec, OutputMode } from "https://deno.land/x/exec/mod.ts";

const result = await exec('bash -c "ls -altr *"',
  { output:OutputMode.StdOut });
console.log(result);
ただそれでもまだ面倒なケースは多いので、私は deno_zx の利用をおすすめします

野良ビルド → 標準ライブラリで可能に

deno bundle test.js > test.js

野良ライブラリの import (npm の代替)

Deno には npm がないのでライブラリのインストール概念が希薄です。 deno.land/x に登録する方法もありますし、GitHub に直接リンクを貼って import もできます。 deno-sqlite を例に挙げてみると、以下のように特定のリビジョンや特定のバージョンも import できます。 node_modules から解法されるのは地味に嬉しい。
import { DB } from "https://deno.land/x/sqlite/mod.ts";
import { DB } from "https://deno.land/x/sqlite@v2.4.0/mod.ts";
import { DB } from "https://raw.githubusercontent.com/dyedgreen/deno-sqlite/master/mod.ts";
import { DB } from "https://raw.githubusercontent.com/dyedgreen/deno-sqlite/v2.4.0/mod.ts";
import { DB } from "https://raw.githubusercontent.com/dyedgreen/deno-sqlite/f9c35896e5a9ec1f375c6172e4e20d7330dfe14f/mod.ts";
コードが増えた時にはバージョンまで記入すると管理が大変なので、 lock files で管理したほうが良さそうです。 Deno は deps.ts に依存性をまとめて書いておくことが推奨されているので、 以下のコマンドでそれを読み取ってキャッシュを保存しておけば、依存性を保存できます。 deps.ts 自体にバージョンは記載されているので、locks.json に保存されるのは比較用のハッシュ値です。 バージョン管理されていないモジュールを使うときには比較用のハッシュ値は必要そうですが、 信頼がおけるソースからロードする場合は、locks.json もなくても良いとわかります。 GitHub にはハッシュ値があるので、最終手段としてはそれでも良さそう。
 deno cache --lock=lock.json --lock-write src/deps.ts
また npm-check-updates のような機能が欲しくなるとも思いますが、udd という便利なやつが出てきたので、これを使うのが良さそうです。


まとめ

標準ライブラリ deno_std が強いので、Node.js より楽に使えます。 セキュリティの話が絡むファイル操作などを Deno.open などの決まった名前空間で利用できるので、セキュリティ管理しやすいし、何より使いやすい。 実行時にセキュリティ権限を許可しないといけないのが面倒だけど、それ以外は良いことしかない。 Node.js より全体的に高機能になっていて、その高機能になった部分は TypeScript で管理されているから、 ブラウザで使いたいと思った時にもビルドすればいける。ビルドも早い。 他言語ならあって当然のものが await を介して自然に使えるようになった結果、かなり使いやすくなった印象です。 await を付け忘れてバグるのは気を付けるしかないけど、いつもストレスが貯まるファイル操作が楽になったのはとても良い。

Deno が一番良いと思ったのは、top-level await と仕様の簡潔さですね。 Node.js はどこまで行っても仕様が複雑で覚えることが多いのと、ベストプラクティスがわからないことが多い。 deno fmt, deno lint といったコマンドで何の準備もなくコードを綺麗にしてくれるのは素晴らしい。 Node.js にも eslint とかのサードパーティプラグインがあったけど、面倒で何もやってなかった。 私は Node.js や Webpack の無限に増え続ける混沌仕様を覚える気はないし、関わらないようにしていたけど、Deno のように全部用意してくれれば従いたくなる。 流行や仕様をよく見ていないと使いこなせない Node.js と違って、Deno はルールの少ない標準ライブラリだけ見れればいい。とても良い。 deno lint に従ってフロントエンドも lint しているとそれなりに綺麗に書けるので、割とおすすめ。 HTML/CSS/JavaScript の実装にもある程度使えて、Deno のエラーが出ないようにするだけで安定した実装ができる。 Go に近い思想で設計されてて、より使いやすく、好感度は高い。

deno lint は長らく unstable だったけど、Deno 1.11 から stable になって本格導入しやすくなってきました。 執筆時点で、unstable で困るのは fs module くらいですかね。 特に気にならないのでもう完全移行できるんじゃないかと思う。

Deno の一番の難点は、Node との互換性のなさ。 同一コードで管理する方法も欲しいですが、私は諦めたのでなるべく早く移行したほうが楽だと思いました。 とは言え最近では、 Node は Deno に寄せ、Deno は Node に寄せるようになってきたので、それほど差がある訳でもないです。 将来的には仕様の綺麗でマスコットのかわいい Node という立ち位置で安定しそうな気がします。

Deno と Node

Deno と Node.js を考えるとき、嫌でもモジュールについて考える必要が出てきます。



モジュールの扱いの違い

Deno はすべてがモジュールとして扱われるけど、Node.js は 基本的にモジュールとしては扱いません。 Node.js は .mjs 拡張子かつ、package.json で { "type" : "module" } を指定した場合に限りモジュールとして動作します。 つまり動作モードが 2つあるのが Node.js なのだけど、動作モードの違いで色々問題が起きる。

(1) モジュールとして動作させる場合 import で読み込み、top-level await が効く。 モジュールとして動作させない場合 require で読み込み、top-level await が効かない。 Node.js は現時点では require を使うのが基本になっているけど、今後は一体どちらをサポートすればいいのかわかりにくい (後述)。 モードの混在は言語仕様としてよろしくない。

(2) モジュールは当然ながら単体で動作します。 これが意味するところは Global Object は使えないということです。 例えば Node.js の __dirname などは Deno に存在しないし、 Node.js もモジュールとして動かそうとすると、__dirname などが使えなくなる。 __dirname は自分で定義しなければいけない。 そのへんが起因となって、モジュールの定義の仕方も違っている。

(3) 再掲しますが、モジュールとして扱うかどうかは package.json に { "type": "module" } があるかどうかで決定します。 package.json をわざわざ作らないといけないので、ちょっとしたスクリプトを書きたいときに困ります。

(4) Node.js では、混在するモードをどのように扱うのでしょうか。package.json 以外にも拡張子で判断します。 .mjs 拡張子で定義されるモジュール群からは、require などを使う .cjs / .js を以下のように呼び出して使えます。
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const cjs = require("test.js");
しかし .cjs から module を呼び出すのは相当大変みたい。 つまり現状では .mjs で作るべきというのはわかったのですが、 これでは既存の遺産がどうなるのかまったく信用できない言語になってるのは致命的と思う。 正直このようなことは考えたくないし、考えさせてはいけないと思う。

Node.js どうすんのこれ

2つのモードができて統一性がなくなってしまうと、無駄に覚えるコストが出てきて良くない。 ブラウザ側を見ても、Chrome と Firefox は既に top-level await に対応しており、Safari も 15 から対応する。 Node.js の require はブラウザの仕組みとズレてきている。

問題を解決しようとするプロジェクトも結構出てきているみたいです。 Deno プロジェクトを Node.js に変換するものであったり、Node.js から Deno globals を呼び出すものがある。 ただ top-level await に動作モードの違いがあるということは、動作モードの異なるライブラリがどんどん混在していくということでもある。

このへんの話を見ていると、Node より Deno のほうがだいぶ楽だなあと思いました。 みんながスーパー Node.js ユーザーではないので、のちのちどうやってサポートされるのかわからんものは困るのです。 Node.js はサポート期間が極めて短い言語なので、Node.js 14 がドロップされた時点で import 側にシフトするという事なのでしょうが、 正直こんなに不安定な状態で突き進んでいるようでは、少し問題を感じますね。 Deno は依存関係周りをどうするのか若干不透明感があるけど、エコシステムが安定しているので、それだけで信頼できる。

Deno はモジュールの作り方がすこし違う

Node.js の node_modules に過度に慣れてしまうと、とりあえず何でもデータをキャッシュしておけばどうにでもなります。 しかし Deno はキャッシュの概念がないので、実装方法も少し変わってきそうです。 例えば何らかのデータベースを読み込む必要がある場合、どうすべきでしょう。

おそらく (1) 実行時に fetch でロードするか、(2) git clone させて実行させるかのどちらか。 毎回ロードするのは面倒なので、2 を想定しているんじゃないかと思います。 ということでデータとモジュールが明確にわかれるのが Deno と言えます。 ただ少し面倒なので 1 もできるように実装して気が付くのは、 Node.js には fetch が存在しないため無駄にライブラリが必要になるということです。 まったく同じ実装にするのは非常に面倒。

で、ライブラリはどう書くのがいいの?

Node.js も Deno も初心者なのでよくわからないけど、以下のように書くのが良いと思いました。 命名規則は Node.js なら main.js / main.mjs、Deno なら mod.js / mod.ts をルートとするのが通例的に良さそう。

Node.js + module (main.mjs)

import * as fs from "fs";
import * as readline from "readline";
import { fileURLToPath } from "url";
import { dirname } from "path";

class YomiDict {
  static async load(filepath) {
    const dict = {};
    if (filepath) {
      const __filename = fileURLToPath(import.meta.url);
      const __dirname = dirname(__filename);
      filepath = __dirname + "/yomi.csv";
    }
    const fileReader = fs.createReadStream(filepath);
    const rl = readline.createInterface({ input:fileReader });
    for await (const line of rl) {
      const arr = line.split(',');
      const word = arr[0];
      const yomis = arr.slice(1);
      dict[word] = yomis;
    }
    const yomiDict = new YomiDict();
    yomiDict.dict = dict;
    return yomiDict;
  }

  constructor() {
    this.dict = {};
  }

  get(word) {
    return this.dict[word];
  }
}

export { YomiDict };

Node.js (main.js)

const fs = require("fs");
const readline = require("readline");

class YomiDict {
  static async load(filepath) {
    const dict = {};
    if (!filepath) {
      filepath = __dirname + "/yomi.csv";
    }
    const fileReader = fs.createReadStream(filepath);
    const rl = readline.createInterface({ input:fileReader });
    for await (const line of rl) {
      const arr = line.split(',');
      const word = arr[0];
      const yomis = arr.slice(1);
      dict[word] = yomis;
    }
    const yomiDict = new YomiDict();
    yomiDict.dict = dict;
    return yomiDict;
  }

  constructor() {
    this.dict = {};
  }

  get(word) {
    return this.dict[word];
  }
}

module.exports = YomiDict;

Deno (mod.js)

import { readLines } from "https://deno.land/std/io/mod.ts";

class YomiDict {
  static async fetch(url) {
    const dict = await fetch(url)
      .then((response) => response.text())
      .then((text) => {
        const d = {};
        text.split("\n").forEach((line) => {
          if (!line) return;
          const arr = line.split(",");
          const word = arr[0];
          const yomis = arr.slice(1);
          d[word] = yomis;
        });
        return d;
      }).catch((e) => {
        console.log(e);
      });
    const yomiDict = new YomiDict();
    yomiDict.dict = dict;
    return yomiDict;
  }

  static async load(filepath) {
    const dict = {};
    if (!filepath) {
      filepath = "./yomi-dict/yomi.csv";
    }
    const fileReader = await Deno.open(filepath);
    for await (const line of readLines(fileReader)) {
      const arr = line.split(",");
      const word = arr[0];
      const yomis = arr.slice(1);
      dict[word] = yomis;
    }
    const yomiDict = new YomiDict();
    yomiDict.dict = dict;
    return yomiDict;
  }

  constructor() {
    this.dict = {};
  }

  get(word) {
    return this.dict[word];
  }
}

export { YomiDict };
将来使うかも知れないので作り方をメモっておきます。 今まで無理やり同期化してたから気付かなかったけど、 ファイル読み込みするとコンストラクタから追い出されるのは、良いんだか悪いんだか…。

2021年7月5日月曜日

ABC の発音練習アプリ Talk ABC を作った

ABC などのアルファベットの発音練習アプリ Talk ABC を作りました。 数日前に Talk Numers を作ったので 30分くらいでできてしまったけど、実用性は高い。 ブラウザの音声読み上げと音声認識をそのまま使っています。



アルファベット単体ではなく、ABC, XYZ, BBACC など、文字数を増やして暗記ゲームにできればよかったけど、 音声認識が文章や単語を想定しているようなので、無理そうでした。 かなりあっさりしたアプリだけど、発音練習には十二分に使い物になります。

ちなみに Desktop / Android の Chrome だけサポートです。 Safari が API をサポートしたと言ってるのでそのうち動きそうですが、今のところ動きません。

2021年7月2日金曜日

英語アプリ Type Numers、Talk Numbers を作った

音声読み上げされた数字をポチポチ入力するゲーム Type Numers を作りました。 また提示された数字を音声入力するゲーム Talk Numbers も一緒に作りました。 ブラウザの音声読み上げと音声認識をそのまま使っています。 環境にもよりますが今でも音声読み上げは 100くらいの言語に対応していて、今後も勝手に増えていきます。



外国語学習でも割と初期からやる数字の数え上げですが、毎回テストするのは芸がない。 シンプルな瞬間暗記ゲームにしたら面白いかなと思って作ってみました。 この手のアプリはまともに作ると殺風景になってしまうので、ネコを歩かせてみました (てきとー)。

Type Numbers は5桁くらいになると英語の練習不足を感じて、少し反応速度が鈍ってきます。 高速に英語を聞き取る練習としては良いかも知れません。 もっと桁数が増えてくるとキツくなってくるので、案外遊べます。 8桁もあれば十分かと思って設定したのですが、もっと桁数多くても良かったかな?

Talk Numbers のほうは結構難しいというか、たまに認識精度が低いかも知れません。 特に one の入力がやたら難しくて死にました。2桁くらいのほうがむしろ簡単。 それ以外の数字なら大丈夫なんですが、WAN とか同じ発音の単語があるからかな? コンテキストを考慮しないと反応しにくいみたいで、one, two, と言えば反応するけど、one 単体だとやたら難しい。 もう 2-3 年もすれば精度も対応ブラウザもマシになると思うので、こちらは将来に期待ですかね。 one apple などと後ろに単語を付ければ簡単に認識するので、英語だけは救済措置を入れておきました。

ちなみに Talk Numbers は Desktop / Android の Chrome だけサポートです。 Safari が API をサポートしたと言ってるのでそのうち動きそうですが、今のところ動きません。

2021年6月24日木曜日

形態素解析と「すもももももももものうち」

「きしゃのきしゃがきしゃできしゃした」は知らなかったので、自分用メモです。 私はこの手の話については「すもももももももものうち」自体がヒューリティクスという認識でいます。

そもそも、すももは「桃のうち」ではなく、実は桃とは異なる、バラ科の別の植物です。 つまり誤文で、私はこれを知った時にがっくり来ました。 ではなぜ「すもも」になるのかと思ったことがあります (キレ気味)。 形態素解析は統計順で行うから「すもも」が正しいのですと言う主張も、納得感がありません。 そもそも間違ってるし、別に統計順を知りたい訳でもない。 どちらかと言えば「酢も藻も桃も股の内」のほうが間違いがない。 まあこれも「も」が多すぎるので lint 的にはあまりよろしくないのですが。

このような問題を考えていくと、完全に意味論の話になってて、形態素解析という枠組みではもう解けない問題です。 よって形態素解析器の平仮名問題は考える必要がなく、日本語の lint で検出すべき問題と思います。 ひらがなの連続回数やひらがなの文字列パターンで、読みやすさが決まることは明白なので、警告を出せば良い。 形態素解析器の精度の議論にも含めてはいけないと思う。 さらに言えばそのへんも考慮して形態素解析すべきなのだと思います。

この話って、たぶん話し言葉の解析に通じます。 話し言葉は Tiny Segmenter で 95% の精度が出て、Mecab も同じくらいの精度です。 そして数GB の rank 情報を無理やり入れても 98% なので、そもそも形態素解析器とは…うごごご…と思ったりします。 平仮名をしっかりすることのほうがよほど重要ではないのかと。 これはずっと思ってることなんですが、Tiny Segmenter 周りってもっと深堀りしたほうが良いんじゃないのかなあ。 3-hop で 95% が出るので詳細なラティスはいらないと思います。 個人的には品詞もうーんと思ったりするのですが、そこはさらに意見が分かれそうかなあ。

いろいろと良い手法が浮かびはするのですが、巷の検証用コーパス自体が有料なので、やる気がしないんですよね…。 誰か Deep でポンしてくれないかなのところがある。

2021年6月20日日曜日

フリゲ紹介: SOLDIERS -DesireWing-(Trial ver)

SOLDIERS -DesireWing-(Trial ver) はまだ体験版ながら、以前から興味のあった長編 (予定?) RPG です。 現在は7章まで3時間くらいの作品ですが、今後に大期待です。



神に機械が禁じられた世界で、機械を作りつづける狂人集団『レア教団』。 それを撃滅するは、最も女を捨てた女…神の使者『ラクトザルド』。

これは古き良き時代の厨二成分が凝縮された芸術作品ですね…。 唐突に次元切断して物語が始まったり (左)、ボスが一瞬で首芸術になったり (右)、 重要人物っぽい人が数分後にコロッとやられてたり、 超絶に濃ゆい文章とストーリーで殴ってくるタイプの RPG です。 かつてこれほどまでに疾走感があるストーリーの RPG はあっただろうか。たぶんない。



なんとなく北斗の拳や忍殺の成分が強いような気がします (下)。 フリゲをやってる人だとメイジの転生録にテイストが近いかも知れませんね。 ストーリー重視の RPG は、やっぱこういう才能爆発系のほうが好みです。



ゲームバランスのほうは唐突に恐竜に襲われて全滅したり、 いきなり壊れ性能っぽい武器防具が手に入ったり、なかなか尖ってます。 しかしサイドビュー+ロマサガっぽい成長システムが実装されてて実はすごい (左)。 バランスも意外と良くて面白かったです。

戦闘面以外でも何気に細かいところに凝ってて、強敵の特徴を死体から観察して対策を取ることができます (右)。 これは今まで見たことなかったのですが、死と隣り合わせの世界観の中ではまさに重要。 すごく良い発想だなと思いました。



だいぶ前のだけど作者様の紹介動画がこちら。この PV 最高です。



完成版が楽しみです。