秋田の飯は、打てた分だけ
流れてくる秋田の郷土料理をローマ字で打つと、その料理を食べたことになる。60秒でコース料金の元が取れたかを会計で判定する。判定はIMEと同じ寛容さで、しはshiでもsiでも、んはnでもnnでも通る。打ち始めた皿は左端で止まって待つので、遅い人でも打った分だけ食べられる。終わったあとに残るのは速さの数字ではなく「どの打鍵を打ち損ねたか」の上位3つ
所要時間4時間15分
制作メモ
- AIに任せた
- ローマ字判定エンジンの設計。かなを1〜2文字のチャンクに切り、チャンクごとに通る書き方の集合を持たせる方式。1つの正解と文字列比較する実装を却下したのは、しょっつるなべ が shottsurunabe でも syotturunabe でも通らないと実用にならないため/んの扱い。n単独で確定してよいかは次のかなで変わる(とんぶり=tonburiは通すが、こんやく=konyakuは通してはいけない)。次チャンクの先頭が母音・n・yのときだけn単独を候補から外す実装にした/nを打った瞬間に確定させない保留の仕組み。完全一致してもより長い候補が生きているうちは確定を待ち、次のキーがどの候補にも当たらなかったときに初めて確定して打ち直す。これが無いとnnと打つ癖の人が全員弾かれる/っを次のチャンクと合体させる実装。がっこ の っこ は kko で1チャンク(かな2文字ぶん)として生成する/値段を打鍵数で決める判断。かなの文字数だと はたはた(4かな8打)が じゅんさい(5かな6打)より安くなり、打つ手間と釣り合わなくなる/ユニットテスト36本(別表記の受理、語尾のん、っの重ね、づとずの区別、押してほしかったキーの通知、皿の投入と取りこぼし、会計、つまずき集計)/ベルトの設計。走行時間を固定値にしていたら、速い人がレーンを空にして1〜2秒なにも打てない時間が生まれた。走行時間=投入間隔×4枚に組み直し、コースが速くなってもベルトの詰まり方は変わらず速さだけが変わる形にした。4枚なのは幅375pxの画面でも皿が重ならない上限だから/?duration= で回を短くしたときにコース料金も同じ比率で按分する判断。60秒ぶんの料金のままだと15秒の回は必ず足が出る=結果画面が嘘になる/体験評価ハーネスを2周まわしたこと(別セッションのOpusがコードを見ずに実際に遊んで10項目で採点)。1周目12/20点で不合格、改善を入れて2周目18/20点で合格/1周目の指摘で自分の難易度計算が間違っていたと分かったこと。式では0.8打/秒で元が取れるはずが実測1.9打/秒必要だった。式に「的が常に左端=残り時間が最短の皿に固定される」という構造が入っておらず、遅い人ほど打ちかけの皿を取り上げられ続けていた/根本対策として「1打でも入れた皿は取り上げない(左端で止まって待つ)」を入れたこと。速度と収穫を切り離す設計で、これによりお手軽は1打/秒でも元が取れるようになった/難易度を式ではなく実際にゲームを1分間回して測るテストに置き換えたこと。ミス率10〜30%の初心者も再現する。式ベースのテストは実測と2倍ずれたので削除した/料理20品ぶんの絵と開始画面のキービジュアルを生成したこと(別のCLIに委譲)。20枚で様式を揃えるため、真俯瞰・器1つ・無地の淡いベージュ背景・やわらかい自然光・文字なし、という共通の作法を1つ書いて4本に分けて並列で回した。合計82KBに落としてある
- 自分でやった
- 「寿司打の秋田バージョン」という題材の指定/提示した4つの舞台(郷土料理の食べ放題/パラソルアイス/難読地名/ハタハタ漁)からの選択
- 失敗と修正
- ゲームの進行テストが5件まとめて落ちた。原因は開始時刻に0を渡していたこと。startedAt が 0 だと if (!startedAt) が真になり、始まっているのに「まだ始まっていない」と判定される。テスト側の値の問題ではなく、実装が0を扱えていない → running という真偽値を別に持たせ、時刻の値で開始判定をしないようにした。ブラウザでも rAF の時刻がページ読み込み直後は0近辺なので、同じ踏み方をしうる形だった
「っは次の子音を重ねる」テストが1件落ちたが、実装ではなくテストの打鍵文字列が間違っていた。しょっつるなべ の っつ を ttu で打つなら「す」は入らないのに、shottusurunabe(tsuとtuを混ぜた形)を期待値にしていた → shotturunabe に直した。ローマ字の別表記を並べるテストは、期待値そのものを間違えやすい
皿の投入テストが、料理名を決め打ちして 'gibasa' と打っていた。皿は山を切って配るので1枚目が何になるかは決まっておらず、別の料理が来ると全打鍵がミスになり、それでも「皿が減っていない」ことは満たすので通ってしまう。落ちて初めて決め打ちに気付いた → いま出ている皿のよみから primaryRomaji でローマ字を組み立てて打つヘルパーに置き換えた。通っていた別のテストも同じ書き方だったので直した
一覧用スクショの1枚目が、レーンに皿が1枚も無い瞬間だった。振り付けが2皿食べたあと250ミリ秒しか待っておらず、次の投入(2秒間隔)より早く撮っていた → 皿が4枚並ぶまで待つヘルパーを足した。ただしこれは撮り方の問題に見えて、実際には「速い人はレーンを空にする」という設計の穴が写っていた
デモ動画でもレーンが空のまま進む時間が目立った。原因は録画スクリプトの打鍵間隔を85ミリ秒=毎秒12打にしていたこと。人が出せない速さで、供給が追いつかないのは当たり前だった → 195ミリ秒(毎秒5打前後)に落とした。あわせてベルトの設計自体も走行時間=投入間隔×4枚に組み直した。速すぎるボットが、本物の設計の穴を先に見つけてくれた形
?duration=15 で短い回を録画したら、コース料金が60秒ぶんの¥3,000のまま残り、どれだけ打っても足が出る結果になった。結果画面が実力ではなく設定の都合で決まってしまう → コース料金を回の長さで按分するようにした(15秒なら¥750)。50円単位に丸めて読みやすくしている
難易度を机上の式で設計したら、実測と2倍以上ずれた。式では「1皿あたり60秒÷必要皿数の時間が使える」と仮定していたが、実際の的は常に左端=残り時間が最も短い皿に固定される。遅い人が打ち始めると数秒で皿が消えて進捗が没収され、次の的もまた消えかけの皿になる。1打/秒でお手軽を遊ぶと18皿中1皿しか食べられず¥900の損だった(体験評価1周目・4シードで再現) → 1打でも入れた皿は取り上げない形にした。左端に着いたらそこで止まり、打ち終わるまで的から外れない。1打も入れていない皿は今までどおり流れて逃すので、時間の緊張は残る。あわせて難易度のテストを、式ではなく実際にゲームを1分間回して測る形に作り直した
「打ち切った瞬間に金額が増えるのが気持ちいい」がこのアプリの狙いなのに、その瞬間の演出を1つも作っていなかった。体験評価1周目で、完成直後175ms分の連続スクショ10枚が1バイトも違わないと指摘された → 皿が上に弾けて消える/金額の右に「+¥350」が浮く/金額そのものが跳ねる、の3つを足した。2周目で「視線は中央の料理名にあるのに演出は左上の金額だけ」と再指摘されたので、中央の表示も緑にフラッシュするようにした
初心者の想定をミス0で置いていた。体験評価2周目で「1打/秒でも正確率79.7%だと¥80の負け」と実測された。実際の初心者は必ず打ち間違える → シミュレーションにミス率を入れて測り直し、お手軽の目標を¥1,000→¥700にした。ミス率30%(正確率70%)でも元が取れる水準。テストもミス率10/20/30%で回している
打ちかけの皿を左端に留めたら、後ろの皿が真下に潜り込んで文字が重なり「ぎじゅばんさい」のように読めなくなった。半透明にしても文字同士は重なるので解決しない → つかんでいる皿はレーンから消し、レーン左端の帯と、下の大きな表示に付けた「つかみ中 — 打ち終わるまで待ちます」で伝える形にした
ローマ字の案内が打鍵の途中で書き換わった。`kiritanpo` の n を打った瞬間に `kiritannpo` へ変わり、押す必要のない2つ目の n を指していた。残りを毎回その場で組み立て直していたのが原因 → 語の頭で決めた綴りを持ち回る方式にした。別表記(si / jyu など)に分岐したときだけ組み直す。案内が最後まで動かないことをテストで固定した
「つかみ中」バッジが、まだ1文字も打っていないのに出たままになった。CSSで `.held-badge { display: inline-block }` と書いたため、ブラウザ既定の `[hidden] { display: none }` を上書きしていた。作者スタイルはUA既定に勝つ → `.held-badge[hidden] { display: none }` を足した。あわせて hidden 属性を使う全要素に display 指定が無いかを機械的に確認した(他には無し)
スマホで1文字も打てなかった。keydown だけを見ており、Androidのソフトキーボードが返す keyCode 229 を「IMEで変換中」とみなして捨てていた。実際には変換していなくても229が来る → beforeinput からも1文字ずつ拾う経路を足し、keyCode 229 の判定をやめた(変換中の判定は isComposing と compositionstart に任せる)。物理キーは preventDefault しているので二重に数えない。両方をE2Eで固定した
生成した料理の絵をそのまま円く切り抜いたら、画像の背景(淡いベージュ)が円の外周に輪として残り、中央の大きな表示が「皿の上に皿が乗っている」ように見えた → 中央を切り詰めて器が円いっぱいになるようにした。1254pxの元画像を消したあとに気付いたので、224pxのWebPから172pxへ切り直している。次に作るときは、縮小する前に切り詰める
















