
先月(10月25日)にリリースした典型一枚謎練習アプリ「Nazorush」の背景や技術情報などをまとめていきます。 不定期で、話題が溜まるごとに執筆する予定です。
Nazorushの主な開発目的は、一枚謎における典型的な処理を練習するためのツールを作成することでした。
謎解き公演に参加していると、一枚謎の典型的な処理に足を取られることが多々ありました。 典型的な処理とは、例えば、五十音表上での文字ずらし、番号からアルファベットへの変換などです。 それらの処理は、題材をランダムアクセスできるほどに暗記していれば瞬時に完了できるものですが、そうでなければ、先頭から順に暗唱したり紙に書き出したりするなどの作業により、無駄に時間を取られてしまう存在になります。
そこで、典型的な処理を一瞬で解けるように練習するためのアプリがほしいと考えるようになりました。 平凡なアイデアながら、自分が調べた限りでは類似アプリが見つからなかったこと、プログラムによる一枚謎の描画に興味があったことから、3月ごろに簡単なプロトタイプを開発しました。 (同月の月報でも軽く触れています。) プロトタイプ開発で満足した私は、その後半年、このアイデアを放置することになります。

9月になりウェブアプリ開発に気が向いた私は、このアプリの開発を再開しました。 ウェブアプリとして一般公開に耐えうるものを作りたかったので、ここで初めてデータベースや認証を導入しました。 UIもかっこよさと見やすさの塩梅を考えて刷新しました。 そしてある程度の機能が揃ったことで、10月25日、リリースするに至りました。

リリースの翌日、Twitter(現X)でアプリのスクリーンショットとリンクをツイートしたところ、翌日にかけて60リツイート、300いいねが付きました。 これまでの経験から、高々10いいねと予想していたので、この広まりぶりには驚きました。 アクセスが一時的ながら急激に増えたことで、利用していたXaaSの無料枠を消費しきってしまったので、一部を有料プランへ切り替えました。
ツイートして間もないとき、知人であるフォロワーから、ツイートボタンがほしいとの要望を受け、急遽実装しました。 他の方々からも度々、要望や不具合の報告があり、その都度開発やタスクの整理を行っていました。
ツイートへの反応が落ち着いてきてからは主に、問題形式の追加と、わかりづらいUIの改善の2点に注力しました。
リリース時に実装されていた問題形式は4種類でした。 その後1ヶ月の開発を経て、この執筆時点では10種類が実装されています。 もともとは典型処理の練習を目的としていたアプリなので、問題形式の追加はさほど重要視していませんでした。 しかし、実際に利用してみると、様々な問題形式をシャッフルして解くのもおもしろいと気づき、増やしたいと考えるようになったわけです。

SNSで利用者の反応を見たところ、アプリの趣旨が伝わりづらいらしいという課題に気づきました。 前述のように、このアプリは典型一枚謎の反復練習を重視しているわけですが、創造性・芸術性の高い謎解きコンテンツを期待する声が散見されたのです。 そこで、つい先日、アプリのホーム画面に簡易的な紹介文を追記しました。
他にも雑多な更新として、挑戦開始フォームや結果画面などのわかりづらかった箇所を継続的に改善しています。
言語とフロントエンドフレームワークには使い慣れたTypeScriptとReactを採用しています。 メタフレームワークとしては当初、以前から好みだったRemixを採用していました。 しかし、React Routerへの統合を受けて先行きが不透明になってきたので、途中で、最もメジャーな選択肢であるNext.jsへの乗り換えを行いました。
デプロイ先にはVercelを採用しています。 他のサービスも検討しましたが、最終的にはとにかく楽であることを重視して選択しました。 実際にはVercel向けにデータベース周りのコードを少し修正する必要がありましたが、それを除けば、簡単なGUI操作で設定が完了したので満足しています。
アプリの重要な要素として一枚謎を自動生成するプログラムがあります。 このプログラムは大まかに次の手順で問題を生成しています。
例えば、問題形式に「五十音表から番号順に文字を拾う問題」が、正解に「いし」が選出された場合、五十音表を描いてから「い」のマスに「1」を、「し」のマスに「2」を書き入れるという単純な仕組みです。 より複雑な問題形式では、手順2で追加のパラメータを生成したり、手順3で表記を変換したりしています。 例えば、最近追加した迷路の問題形式では、手順2で山登り法での探索を実行しています。
一枚謎の描画にはフロントエンドと同じくReactを使用しています。
具体的には、ReactコンポーネントでSVGを構築した上でreact-dom/serverのrenderToStaticMarkupを実行しています。
Reactを採用することで、わかりやすく宣言的に描画プログラムを実装できています。
開発初期にはテンプレートエンジンなども候補にありましたが、エコシステムがReactほど充実していない点、実装中にコンテキストスイッチが発生する点が気になり採用を見送りました。
描画する図形・テキストの座標計算では、部分的に、レイアウトエンジンYogaの力を借りています。 フレックスレイアウトのような座標計算を自前で実装するとソースコードが複雑になってしまうからです。 下図に見られる灰色の長方形は、Yogaで計算したレイアウトをデバッグ用に可視化した結果になっています。
ちなみに、当然ですが単にフレックスレイアウトを使用したい場合、HTML+CSSを利用できるのであればそれらに頼ったほうが実装は楽に済みます。 私も、ボードゲームのプロトタイプを作成する場面などではHTML+CSSを採用しています。

簡単にまとめると次のようなタスクがあります。 ただし、実際に着手するか否かは、私のモチベーションとリソース次第。
その他、開発環境やCIなどを整備するタスクも残っていますが、ここでは割愛します。
問題形式の追加は楽しい作業なので、今後もできる限り続けたいと考えています。 問題形式の中でも、実装済みのものの類題であれば短時間で実装できます。 例えば、N個ある物の一覧から文字を拾う問題は、既存の曜日や十二支の問題の延長として実装しやすいのです。 一方で、次のような形式は、可能ながら技術的なハードルがあり、実装に時間を要する見込みです。
生成器に与えるパラメータを、ランダムに選出する代わりにユーザからの入力とすることで、一枚謎の作成を補助するツールとしても利用できると考えています。 しかし、実現にはいくつかの課題があります。
趣味で気ままに開発しているアプリではありますが、アクセスの増加を受けて、収支のバランスを考慮するようになりました。 月数千円〜一万円程度の収入があるとXaaSで利用できる機能の幅が広がります。
現在のところ、開発運用コストが大きい一部の機能のみを有料化するサブスクリプション(フリーミアム)が現実的だろうと考えています。 広告は個人的に好みではないので避けたいです。 noteやpixivFANBOXなどでの支援の募集も検討していましたが、それらのサービスのWYSIWYG執筆環境が気に入らなかったこと、継続的に提供できるコンテンツが無いことから、実施の可能性が低くなっています。
このアプリに興味を持っていただけたこと、ありがたい限りです。 要望などございましたら、Nazorush関連の私の作業ツイートにお気軽にリプライください。