▍AI Dev Lab
AdageQuest

AdageQuest ができるまで — ことわざを 5 層で覚えるクイズ

ことわざクイズの開発ログ。 48 件を漢字・読み・意味・中国古典の出典・英訳の 5 層で持たせ、 読み当て / 意味当て / 穴埋め / 故事から当てる の 4 モード × 3 難度で出題する仕組みを記録しています。

AdageQuest は、ことわざと故事成語を「漢字・読み・意味・出典・英訳」の 5 層で覚えるクイズです。48 件を厳選し、4 つの出題モード × 3 難度で回します。出題も採点も端末内で完結します。

#🎯 意味を当てるだけでは覚えられない

ことわざアプリの多くは「意味を 4 択から選ぶ」形式です。でもそれだけだと、選択肢の消去法で正解できてしまい、記憶に残りません。

そこで 1 つのことわざに複数の面を持たせました。

層例(呉越同舟)
漢字呉越同舟
読みごえつどうしゅう
意味仲の悪い者どうしが同じ場に居合わせること
出典『孫子』九地篇
英訳strange bedfellows

そして出題モードを層の組み合わせで作ります。

  1. 読み当て — 漢字を見て読みを選ぶ
  2. 意味当て — 意味を選ぶ
  3. 穴埋め — 一部を隠して漢字を選ぶ
  4. 故事 → ことわざ — 由来のエピソードから逆引き

同じ 48 件でも、問われる面が変わると別の問題になります。データを増やさずに問題数を増やせる構造です。

#🏗️ 誤答をデータから作る

4 択の作り方が地味に重要でした。誤答(ディストラクタ)は、他のことわざの同じ層から取ってきます。

ts
const distractors = sampleN(
  ADAGES.filter((a) => a.kanji !== picked.kanji),
  3,
  rng,
).map((a) => a.yomi)
const choices = shuffle([picked.yomi, ...distractors], rng)
return {
  prompt: picked.kanji,
  choices,
  answerIdx: choices.indexOf(picked.yomi),
  ...
}

これで誤答も実在することわざの読みになります。適当な文字列を並べると、明らかにおかしい選択肢ができて消去法が通ってしまう。「どれもありそう」に見える誤答でないと、クイズとして機能しません。

answerIdx をシャッフル後に indexOf で求めているのもポイントで、正解の位置を先に決めない。位置に偏りがあると、慣れた人が場所で当てられます。

#🎚️ 難度が足りないときに隣から借りる

3 難度に分けると、あるレベルの母数が 4 件を切ることがあります。そのままだと 4 択が作れません。

ts
let pool = ADAGES.filter((a) => a.level === level)
if (pool.length < 4) {
  pool = ADAGES.filter((a) =>
    level === "easy" ? a.level !== "hard" : level === "hard" ? a.level !== "easy" : true,
  )
}

足りなければ隣接する難度まで広げる、というフォールバックです。easy なら hard 以外、hard なら easy 以外。難度が 2 段飛ばしにならないようにしています。

データを足せば解消しますが、データ量に依存しない形にしておくと、後から件数を増減しても壊れません。

#📚 出典を調べるのが本体だった

実装より時間がかかったのが、48 件それぞれの出典を確かめる作業でした。

『論語』『荀子』『荘子』『列子』『史記』『戦国策』『韓非子』『淮南子』『後漢書』『晋書』『詩経』『春秋左氏伝』『景徳伝燈録』── ことわざ辞典に載っている出典を、篇名まで含めて確認していきます。

ここで方針を決めました。出典が特定できないものは載せない。 由来が諸説あるもの、後世の創作とされるものは外しました。48 件という数は「確認が取れたもの」の結果で、最初から目標にした数ではありません。

「数字や事実を出すなら、確かめられる範囲だけにする」というのは他のサービスでも同じで、干支の日柱を実装しなかったのも同じ理由です。

#🎲 シード付き乱数で出題を再現する

ts
export function makeRng(seed: number): () => number

出題順もシードから作ります。「さっきの問題をもう一度」ができるのと、同じ種を共有すれば同じセットを解けるためです。学習系では、この再現性が復習の導線になります。

自己ベストは localStorage に保存していて、サーバーには何も送りません。

#😓 苦労したところ

故事の要約をどこまで書くか。 「臥薪嘗胆」の背景を全部書くと長すぎ、削ると意味が通りません。登場人物と、何が起きて、どう転んだかの 3 要素だけに絞り、2〜3 文に収めました。達磨、太公望、孔子、杜牧、項羽、廉頗と藺相如、管仲と鮑叔、伯牙と鍾子期 ── どれも面白い話なので、削るのが惜しい作業でした。

英訳の付け方。 直訳すると意味が通らず、意訳すると別のことわざになります。対応する英語のことわざがあるものはそれを当て、無いものは意味の説明にしました。「一石二鳥 = kill two birds with one stone」のようにそのまま対応するものは、実は多くありません。

4 択の選択肢の長さ。 意味当てモードは選択肢が長文になり、スマートフォンだと縦に伸びます。文字数の近いものを優先して選ぶようにして、長さで正解が推測されないようにもしました。

#🔭 今後の拡張

  • 件数を増やす(出典の確認が取れた順に)
  • 間違えた問題だけを復習するモード
  • 四字熟語・慣用句への拡張
  • 読み上げ(音で覚える)

#💡 このサービスから言えること

学習コンテンツは、データの持ち方で問題数が決まります。 1 件に 5 層を持たせたから、48 件で 4 モード分の問題が作れました。「問題」を直接データにしていたら、192 問を手で書くことになっていたはずです。

そして誤答の質がクイズの質です。 正解を用意するより、「ありそうな間違い」を用意するほうが難しく、そこが手応えを決めます。他の実データから借りてくるのが、いちばん確実な方法でした。

[ ./next_action ]

読んだら、 AdageQuest を実際に動かす。

この開発ログは AdageQuest をどう作ったかの記録です。 読み終わったらそのままサービス本体へ戻って、 実物で価値を確かめてください。

[ ./related_logs ]

関連する開発ログ

all logs →
ShadowTwin

立体パズルの別解を作る:同じ影と最少の積み木

ShadowTwinの開発記録。3方向の図から別々の最少配置を2つ探す仕組み、5問の独立全探索、スマホの横並び比較、保存・再編集と入力検査を解説します。実照明の計算や能力診断ではありません。

read log →
SlitCard

スリットアニメーションを作る:4コマの合成と透明原稿の設計

SlitCardの開発記録。4コマの帯を交互に合成し、すき間と黒帯で別のコマを見せる仕組みを解説。同縮尺の台紙・透明シートSVG、50mm確認線、手動プレビュー・初期非表示、再編集と実出力検査を整理します。

read log →
WindowLight

日当たりシミュレーションを作る:窓の光を床へ投影する幾何モデル

WindowLightの開発記録。太陽位置近似から窓の4隅を床へ投影し、凸多角形を部屋で切り取る設計を解説。国立天文台の16時刻との限定照合、大気差・低高度・実際の採光との違い、SVGと設定の再編集を整理します。

read log →
GridLights

電源構成の比較を作る:割合と実量を100個の電球で読み解く

電源構成比較GridLightsの開発記録。12か国の年次発電データを最大剰余法で100個の電球に変換し、割合とTWhを比較する設計を解説。欠損を0補完しない7分類、予想と実値、出典付きSVGと実データ検証を整理します。

read log →
SoundLoom

音のお絵かきを作る:実WAVから絵を測り直す設計

音のお絵かきの開発記録。描画を正弦波のPCMとWAVへ変換し、保存音声の周波数をGoertzel法で測って比較する仕組みを解説。音量・測定窓・量子化の限界と、編集・離脱時の古い結果を抑止する検証を整理します。

read log →
MazeLoom

宝探し迷路を作る:寄り道まで含めた最短ルートの探索

宝探し迷路の開発記録。通路を木として生成し、現在地と3つの宝の取得状態を組み合わせて最短ルートを探す仕組みを解説。問題コードの版固定、画面と解答付きPDFの一致、手数と印刷の検証を整理します。

read log →