aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2023-07-21 18:37:09 +0200
committerAlex Auvolat <alex@adnab.me>2023-07-21 18:37:09 +0200
commit41ab1f6eb77b0c3fe24577f2daec901cedb0ad60 (patch)
tree3452fc0290ac78b25957112646d1e5ed244bb3f6
parente220b38123fcecbf4448826f3f0ca2098c89181f (diff)
downloaddatagengo-41ab1f6eb77b0c3fe24577f2daec901cedb0ad60.tar.gz
datagengo-41ab1f6eb77b0c3fe24577f2daec901cedb0ad60.zip
Some batches
-rw-r--r--.gitignore2
-rw-r--r--Cargo.lock177
-rw-r--r--Cargo.toml4
-rw-r--r--data/batches.json1578
-rw-r--r--data/kanji_levels.txt50
-rw-r--r--html/style.css24
-rw-r--r--src/main.rs169
7 files changed, 1947 insertions, 57 deletions
diff --git a/.gitignore b/.gitignore
index 6fd94d6..6067da8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,5 @@
kanjidic*.xml
JMdict*.xml
examples.utf
+
+html/*.html
diff --git a/Cargo.lock b/Cargo.lock
index f5ba8c2..768e6cc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -23,18 +23,30 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -50,15 +62,67 @@ dependencies = [
]
[[package]]
+name = "crossbeam-channel"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
name = "datagengo"
version = "0.1.0"
dependencies = [
"anyhow",
+ "rayon",
"roxmltree",
+ "serde",
+ "serde_json",
"structopt",
]
[[package]]
+name = "either"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+
+[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -77,6 +141,18 @@ dependencies = [
]
[[package]]
+name = "hermit-abi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -89,6 +165,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi 0.3.2",
+ "libc",
+]
+
+[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -97,7 +192,7 @@ dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
"version_check",
]
@@ -131,6 +226,28 @@ dependencies = [
]
[[package]]
+name = "rayon"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "num_cpus",
+]
+
+[[package]]
name = "roxmltree"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -140,6 +257,49 @@ dependencies = [
]
[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "serde"
+version = "1.0.174"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b88756493a5bd5e5395d53baa70b194b05764ab85b59e43e4b8f4e1192fa9b1"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.174"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e5c3a298c7f978e53536f95a63bdc4c4a64550582f31a0359a9afda6aede62e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.27",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -166,7 +326,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
]
[[package]]
@@ -181,6 +341,17 @@ dependencies = [
]
[[package]]
+name = "syn"
+version = "2.0.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index f00cad3..2edae91 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,5 +7,9 @@ edition = "2021"
[dependencies]
anyhow = "1.0"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
structopt = "0.3"
+rayon = "1.7"
roxmltree = "0.18"
+
diff --git a/data/batches.json b/data/batches.json
new file mode 100644
index 0000000..cf8c792
--- /dev/null
+++ b/data/batches.json
@@ -0,0 +1,1578 @@
+[
+ {
+ "level": "N4-1",
+ "chars": [
+ "一",
+ "上",
+ "五",
+ "人",
+ "先",
+ "入",
+ "出",
+ "大",
+ "天",
+ "学",
+ "年",
+ "手",
+ "日",
+ "木",
+ "本",
+ "気",
+ "生",
+ "目",
+ "見",
+ "金"
+ ],
+ "chars_p1": [],
+ "chars_p2": [],
+ "chars_bad": [],
+ "examples": [
+ {
+ "ja": "木は一本も見えなかった。",
+ "en": "There was not a tree in sight.",
+ "expl": "木(き) は 一本 も 見える(みえる){見えなかった}",
+ "id": "ID=323622_80096",
+ "chars": [
+ "一",
+ "木",
+ "本",
+ "見"
+ ]
+ },
+ {
+ "ja": "この人は目が見えなくなって五年になる。",
+ "en": "This man has been blind for five years at least.",
+ "expl": "此の{この} 人(ひと) は 目が見える{目が見えなく} 成る[01]{なって} 五 年(ねん) になる[01]",
+ "id": "ID=58320_220999",
+ "chars": [
+ "五",
+ "人",
+ "年",
+ "目",
+ "見"
+ ]
+ },
+ {
+ "ja": "あなたはそんな大金をどうやって手に入れたのですか。",
+ "en": "How did you come by such a big sum of money?",
+ "expl": "貴方(あなた)[01]{あなた} は そんな 大金 を どうやって 手に入れる{手に入れた} のです か",
+ "id": "ID=69907_232537",
+ "chars": [
+ "入",
+ "大",
+ "手",
+ "金"
+ ]
+ },
+ {
+ "ja": "その先生は大学を出たばかりだ。",
+ "en": "The teacher is fresh from college.",
+ "expl": "其の[01]{その} 先生(せんせい)[01] は 大学を出る{大学を出た} 許り[03]{ばかり} だ",
+ "id": "ID=45789_208529",
+ "chars": [
+ "先",
+ "出",
+ "大",
+ "学",
+ "生"
+ ]
+ },
+ {
+ "ja": "その日は上天気となった。",
+ "en": "The day turned out to be fine.",
+ "expl": "其の[01]{その} 日(ひ)[01] は 上天気~ となる{となった}",
+ "id": "ID=44585_207333",
+ "chars": [
+ "上",
+ "天",
+ "日",
+ "気"
+ ]
+ }
+ ]
+ },
+ {
+ "level": "N4-1",
+ "chars": [
+ "七",
+ "三",
+ "下",
+ "中",
+ "二",
+ "円",
+ "十",
+ "千",
+ "右",
+ "四",
+ "女",
+ "子",
+ "山",
+ "左",
+ "月",
+ "校",
+ "男",
+ "空",
+ "足",
+ "車"
+ ],
+ "chars_p1": [
+ "一",
+ "五",
+ "人",
+ "入",
+ "出",
+ "学",
+ "日",
+ "本",
+ "気",
+ "金"
+ ],
+ "chars_p2": [],
+ "chars_bad": [],
+ "examples": [
+ {
+ "ja": "このクラスには15人の男子と28人の女子がいる。",
+ "en": "This class consists of 15 boys and 28 girls.",
+ "expl": "此の{この} クラス には 人(にん) の 男子[01] と 人(にん) の 女子(じょし)~ が 居る(いる)[01]{いる}",
+ "id": "ID=61194_223858",
+ "chars": [
+ "人",
+ "女",
+ "子",
+ "男"
+ ]
+ },
+ {
+ "ja": "この本は三千円する。",
+ "en": "This book costs 3,000 yen.",
+ "expl": "此の{この} 本(ほん)[01] は 三 千円 為る(する)[08]{する}",
+ "id": "ID=56952_219631",
+ "chars": [
+ "三",
+ "円",
+ "千",
+ "本"
+ ]
+ },
+ {
+ "ja": "山の中では空気がとてもきれいだ。",
+ "en": "The air is very pure in the mountains.",
+ "expl": "山(やま)[01] の 中(なか) で(#2028980) は 空気 が 迚も[01]{とても} 奇麗{きれい} だ",
+ "id": "ID=245068_169418",
+ "chars": [
+ "中",
+ "山",
+ "気",
+ "空"
+ ]
+ },
+ {
+ "ja": "人の噂も七十五日。",
+ "en": "A wonder lasts but nine days.",
+ "expl": "人(ひと) の 噂 も 七十 五 日(ひ)[01]",
+ "id": "ID=269938_144626",
+ "chars": [
+ "七",
+ "五",
+ "人",
+ "十",
+ "日"
+ ]
+ },
+ {
+ "ja": "入るお金が右から左へと出てしまう。",
+ "en": "I spend money as soon as I get it.",
+ "expl": "入る(はいる) お金 が 右[01] から 左(ひだり) へと 出る{出て} 仕舞う{しまう}",
+ "id": "ID=281910_122093",
+ "chars": [
+ "入",
+ "出",
+ "右",
+ "左",
+ "金"
+ ]
+ },
+ {
+ "ja": "三に二を足すと五になる。",
+ "en": "Add two to three, and you get five.",
+ "expl": "三 に 二 を[01] 足す[01] と 五 になる[01]",
+ "id": "ID=244976_169510",
+ "chars": [
+ "三",
+ "二",
+ "五",
+ "足"
+ ]
+ },
+ {
+ "ja": "山を下ったところに学校がある。",
+ "en": "There is a school down the mountain.",
+ "expl": "山(やま)[01] を[03] 下る(くだる)[01]{下った}~ 所(ところ){ところ} に 学校 が[01] 有る{ある}",
+ "id": "ID=245081_169405",
+ "chars": [
+ "下",
+ "学",
+ "山",
+ "校"
+ ]
+ },
+ {
+ "ja": "四月一日です。",
+ "en": "It's April first.",
+ "expl": "四月(しがつ) 一日(ついたち)[01] です",
+ "id": "ID=245523_168964",
+ "chars": [
+ "一",
+ "四",
+ "日",
+ "月"
+ ]
+ },
+ {
+ "ja": "日本車は右ハンドルです。",
+ "en": "Japanese cars are right-hand drive.",
+ "expl": "日本車 は 右ハンドル~ です",
+ "id": "ID=327900_75818",
+ "chars": [
+ "右",
+ "日",
+ "本",
+ "車"
+ ]
+ }
+ ]
+ },
+ {
+ "level": "N4-1 + N4-2",
+ "chars": [
+ "九",
+ "休",
+ "八",
+ "六",
+ "口",
+ "名",
+ "土",
+ "小",
+ "川",
+ "水",
+ "火",
+ "白",
+ "立",
+ "耳",
+ "花",
+ "行",
+ "週",
+ "間",
+ "雨",
+ "高"
+ ],
+ "chars_p1": [
+ "中",
+ "右",
+ "女",
+ "子",
+ "校"
+ ],
+ "chars_p2": [
+ "一",
+ "入",
+ "出",
+ "大",
+ "学",
+ "年",
+ "日",
+ "本",
+ "生"
+ ],
+ "chars_bad": [
+ "引"
+ ],
+ "examples": [
+ {
+ "ja": "たとえ火の中水の中。",
+ "en": "Go through fire and water.",
+ "expl": "仮令(たとえ){たとえ} 火の中水の中",
+ "id": "ID=40745_203507",
+ "chars": [
+ "中",
+ "水",
+ "火"
+ ]
+ },
+ {
+ "ja": "リンダ・ウッドが入口に立っていました。",
+ "en": "Linda Wood was standing at the door.",
+ "expl": "が 入り口{入口} に 立つ{立っていました}",
+ "id": "ID=29608_192444",
+ "chars": [
+ "入",
+ "口",
+ "立"
+ ]
+ },
+ {
+ "ja": "大雨で川があふれた。",
+ "en": "The river overflowed because of the heavy rain.",
+ "expl": "大雨 で(#2028980) 川[01] が 溢れる(あふれる){あふれた}",
+ "id": "ID=275553_137683",
+ "chars": [
+ "大",
+ "川",
+ "雨"
+ ]
+ },
+ {
+ "ja": "このバラは白い花をつける。",
+ "en": "This rose has a white bloom.",
+ "expl": "此の{この} 薔薇{バラ} は 白い 花[01] を 付ける{つける}",
+ "id": "ID=60641_223309",
+ "chars": [
+ "白",
+ "花"
+ ]
+ },
+ {
+ "ja": "その休日にはすべての学校が休みになる。",
+ "en": "All the schools are closed on that holiday.",
+ "expl": "其の[01]{その} 休日 には 全て{すべての} 学校 が 休み[02] になる[01]",
+ "id": "ID=48726_211450",
+ "chars": [
+ "休",
+ "学",
+ "日",
+ "校"
+ ]
+ },
+ {
+ "ja": "アンは小さな女の子です。",
+ "en": "Ann is a little girl.",
+ "expl": "は 小さな 女の子(おんなのこ) です",
+ "id": "ID=66840_229481",
+ "chars": [
+ "女",
+ "子",
+ "小"
+ ]
+ },
+ {
+ "ja": "右耳からうみが出ます。",
+ "en": "Pus is coming out of my right ear.",
+ "expl": "右[01] 耳[01] から 膿{うみ}~ が 出る{出ます}",
+ "id": "ID=27065_189909",
+ "chars": [
+ "出",
+ "右",
+ "耳"
+ ]
+ },
+ {
+ "ja": "かれは土に水をやった。",
+ "en": "He watered the soil.",
+ "expl": "彼(かれ){かれ} は 土(つち) に 水をやる{水をやった}",
+ "id": "ID=518855_518854",
+ "chars": [
+ "土",
+ "水"
+ ]
+ },
+ {
+ "ja": "とにかく一か八かやってみる。",
+ "en": "Anyway, I'll take a chance.",
+ "expl": "兎に角{とにかく} 一か八か やって見る{やってみる}",
+ "id": "ID=37661_200459",
+ "chars": [
+ "一",
+ "八"
+ ]
+ },
+ {
+ "ja": "バンド名はレッド・ホット。",
+ "en": "My band name's Red Hot.",
+ "expl": "バンド[01] 名(な) は NI{レッドホット}",
+ "id": "ID=34940_197752",
+ "chars": [
+ "名"
+ ]
+ },
+ {
+ "ja": "九引く六はいくつですか。",
+ "en": "How many is nine minus six?",
+ "expl": "九 引く 六 は 幾つ{いくつ} ですか",
+ "id": "ID=519105_519104",
+ "chars": [
+ "九",
+ "六",
+ "引"
+ ]
+ },
+ {
+ "ja": "日本の高校生は年に35週間学校に行く。",
+ "en": "Japanese high school students go to school 35 weeks a year.",
+ "expl": "日本 の 高校生 は 年(とし)[01] に 週間 学校 に 行く",
+ "id": "ID=281384_122619",
+ "chars": [
+ "学",
+ "年",
+ "日",
+ "本",
+ "校",
+ "生",
+ "行",
+ "週",
+ "間",
+ "高"
+ ]
+ }
+ ]
+ },
+ {
+ "level": "N4-2",
+ "chars": [
+ "今",
+ "午",
+ "国",
+ "外",
+ "多",
+ "少",
+ "後",
+ "時",
+ "書",
+ "来",
+ "百",
+ "行",
+ "話",
+ "語",
+ "読",
+ "週",
+ "長",
+ "間",
+ "電",
+ "高"
+ ],
+ "chars_p1": [
+ "八",
+ "行",
+ "週",
+ "間",
+ "高"
+ ],
+ "chars_p2": [
+ "校"
+ ],
+ "chars_bad": [],
+ "examples": [
+ {
+ "ja": "日本の高校生は年に35週間学校に行く。",
+ "en": "Japanese high school students go to school 35 weeks a year.",
+ "expl": "日本 の 高校生 は 年(とし)[01] に 週間 学校 に 行く",
+ "id": "ID=281384_122619",
+ "chars": [
+ "学",
+ "年",
+ "日",
+ "本",
+ "校",
+ "生",
+ "行",
+ "週",
+ "間",
+ "高"
+ ]
+ },
+ {
+ "ja": "1時間後にまたお電話します。",
+ "en": "I'll call you up again in an hour.",
+ "expl": "一時間{1時間} 後(ご) に 又{また} 御(お){お} 電話 為る(する)[11]{します}",
+ "id": "ID=73154_235774",
+ "chars": [
+ "後",
+ "時",
+ "話",
+ "間",
+ "電"
+ ]
+ },
+ {
+ "ja": "外国語をマスターするには長い時間がかかる。",
+ "en": "It takes us a long time to master a foreign language.",
+ "expl": "外国語 を マスター[01] 為る(する){する} には 長い 時間[01] が 掛かる[01]{かかる}",
+ "id": "ID=247224_167277",
+ "chars": [
+ "国",
+ "外",
+ "時",
+ "語",
+ "長",
+ "間"
+ ]
+ },
+ {
+ "ja": "今日の午後多くの人がやって来ることになっている。",
+ "en": "There are many people to come this afternoon.",
+ "expl": "今日 の 午後 多く の 人(ひと) が やって来る ことになっている",
+ "id": "ID=242559_171916",
+ "chars": [
+ "人",
+ "今",
+ "午",
+ "多",
+ "後",
+ "日",
+ "来"
+ ]
+ },
+ {
+ "ja": "このごろは読書する時間が少しはある。",
+ "en": "I have a little time for reading these days.",
+ "expl": "此の頃(このごろ){このごろ} は 読書 為る(する){する} 時間[01] が 少し は 有る{ある}",
+ "id": "ID=61080_223744",
+ "chars": [
+ "少",
+ "時",
+ "書",
+ "読",
+ "間"
+ ]
+ },
+ {
+ "ja": "あのレースは八百長だった。",
+ "en": "The race was fixed.",
+ "expl": "彼の(あの){あの} レース(#1144380)~ は 八百長(やおちょう)~ だ{だった}",
+ "id": "ID=68601_231233",
+ "chars": [
+ "八",
+ "百",
+ "長"
+ ]
+ }
+ ]
+ },
+ {
+ "level": "N4-2",
+ "chars": [
+ "会",
+ "何",
+ "分",
+ "前",
+ "北",
+ "半",
+ "南",
+ "友",
+ "店",
+ "新",
+ "母",
+ "毎",
+ "父",
+ "社",
+ "聞",
+ "言",
+ "買",
+ "道",
+ "食",
+ "魚"
+ ],
+ "chars_p1": [
+ "午",
+ "国",
+ "外",
+ "時",
+ "行",
+ "話",
+ "長",
+ "間"
+ ],
+ "chars_p2": [
+ "名",
+ "行",
+ "間"
+ ],
+ "chars_bad": [],
+ "examples": [
+ {
+ "ja": "うちの会社にも何人か外国の人がいます。",
+ "en": "There are some foreign workers in my company as well.",
+ "expl": "うちの会社 にも 何人か 外国 の 人(ひと) が 居る(いる)[01]{います}",
+ "id": "ID=65575_228221",
+ "chars": [
+ "人",
+ "会",
+ "何",
+ "国",
+ "外",
+ "社"
+ ]
+ },
+ {
+ "ja": "エドウィンと言う名前を聞いてもピンとこない。",
+ "en": "The name Edwin doesn't ring a bell.",
+ "expl": "と 言う 名前[01] を 聞く{聞いて} も ぴんと来る{ピンとこない}",
+ "id": "ID=65314_227961",
+ "chars": [
+ "前",
+ "名",
+ "聞",
+ "言"
+ ]
+ },
+ {
+ "ja": "父は新しい車を買いました。",
+ "en": "My father has bought a new car.",
+ "expl": "父 は 新しい 車(くるま) を 買う[01]{買いました}",
+ "id": "ID=319284_84431",
+ "chars": [
+ "新",
+ "父",
+ "買",
+ "車"
+ ]
+ },
+ {
+ "ja": "メロンが半分食べられた。",
+ "en": "Half of the melon was eaten.",
+ "expl": "メロン が 半分(はんぶん) 食べる{食べられた}",
+ "id": "ID=31756_194582",
+ "chars": [
+ "分",
+ "半",
+ "食"
+ ]
+ },
+ {
+ "ja": "日本は南北に長い国です。",
+ "en": "Japan is a very long country from north to south.",
+ "expl": "日本 は 南北~ に 長い 国[01] です",
+ "id": "ID=281594_122409",
+ "chars": [
+ "北",
+ "南",
+ "国",
+ "日",
+ "本",
+ "長"
+ ]
+ },
+ {
+ "ja": "母は毎日うちにいます。",
+ "en": "Mother stays at home every day.",
+ "expl": "母(はは) は 毎日 内(うち){うち} に 居る(いる)[02]{います}",
+ "id": "ID=320861_82855",
+ "chars": [
+ "日",
+ "母",
+ "毎"
+ ]
+ },
+ {
+ "ja": "友人たちと話す時間がたくさんあった。",
+ "en": "I had plenty of time to talk to many friends.",
+ "expl": "友人 達{たち} と 話す 時間 が 沢山{たくさん} 有る{あった}",
+ "id": "ID=324338_79379",
+ "chars": [
+ "人",
+ "友",
+ "時",
+ "話",
+ "間"
+ ]
+ },
+ {
+ "ja": "「外国では魚を生で食べない」というのは嘘だ。",
+ "en": "It's a lie that, \"they don't eat fish raw abroad.\"",
+ "expl": "外国 で(#2028980) は 魚 を 生(なま) で(#2028980) 食べる{食べない} と言う{という} の は 嘘 だ",
+ "id": "ID=412071_412070",
+ "chars": [
+ "国",
+ "外",
+ "生",
+ "食",
+ "魚"
+ ]
+ },
+ {
+ "ja": "店は午前9時にあきます。",
+ "en": "The store opens at 9 a.m.",
+ "expl": "店(みせ) は 午前 時(じ)[01] に 起きる{あきます}",
+ "id": "ID=279009_124985",
+ "chars": [
+ "前",
+ "午",
+ "店",
+ "時"
+ ]
+ },
+ {
+ "ja": "この道を5分行って下さい。",
+ "en": "Go along this street about five minutes.",
+ "expl": "此の{この} 道(みち) を 分(ふん) 行く[01]{行って} 下さい",
+ "id": "ID=57618_220298",
+ "chars": [
+ "下",
+ "分",
+ "行",
+ "道"
+ ]
+ }
+ ]
+ },
+ {
+ "level": "N4-2 + N4-3 + N3-1",
+ "chars": [
+ "万",
+ "力",
+ "古",
+ "夕",
+ "字",
+ "安",
+ "文",
+ "早",
+ "村",
+ "東",
+ "林",
+ "森",
+ "犬",
+ "田",
+ "町",
+ "西",
+ "赤",
+ "音",
+ "飲",
+ "駅"
+ ],
+ "chars_p1": [
+ "会",
+ "何",
+ "前",
+ "店",
+ "父",
+ "社",
+ "聞",
+ "言",
+ "買",
+ "道",
+ "食"
+ ],
+ "chars_p2": [
+ "国",
+ "外",
+ "書",
+ "来",
+ "百",
+ "週",
+ "長",
+ "間"
+ ],
+ "chars_bad": [],
+ "examples": [
+ {
+ "ja": "ジョンが中古車を買ったのは先週であった。",
+ "en": "It was last week that John bought a second-hand car.",
+ "expl": "が 中古車 を 買う[01]{買った} の は 先週 である{であった}",
+ "id": "ID=1832216_215516",
+ "chars": [
+ "中",
+ "先",
+ "古",
+ "買",
+ "車",
+ "週"
+ ]
+ },
+ {
+ "ja": "もし百万ドルあれば、どうしますか。",
+ "en": "What would you do if you had a million dollars?",
+ "expl": "若し(もし){もし} 百万 弗[01]{ドル} 有る{あれば} どうするか{どうしますか}",
+ "id": "ID=30630_193464",
+ "chars": [
+ "万",
+ "百"
+ ]
+ },
+ {
+ "ja": "フランスは西ヨーロッパにある。",
+ "en": "France is in western Europe.",
+ "expl": "仏蘭西{フランス} は 西ヨーロッパ に 有る{ある}",
+ "id": "ID=34175_196995",
+ "chars": [
+ "西"
+ ]
+ },
+ {
+ "ja": "月は東から上る。",
+ "en": "The moon rises in the east.",
+ "expl": "月(つき)[01] は 東(ひがし) から 登る[02]{上る}",
+ "id": "ID=238836_175628",
+ "chars": [
+ "上",
+ "月",
+ "東"
+ ]
+ },
+ {
+ "ja": "この本は駅前の書店で買ったんだ。",
+ "en": "I bought this book at the bookstore in front of the station.",
+ "expl": "此の{この} 本(ほん)[01] は 駅前~ の 書店 で(#2028980) 買う[01]{買った} のだ{んだ}",
+ "id": "ID=56991_219670",
+ "chars": [
+ "前",
+ "店",
+ "書",
+ "本",
+ "買",
+ "駅"
+ ]
+ },
+ {
+ "ja": "たいてい外で飲み会をします。",
+ "en": "We usually go out for drinking parties.",
+ "expl": "大抵[01]{たいてい} 外(そと) で(#2028980) 飲む[01]{飲み} 会(かい) を 為る(する){します}",
+ "id": "ID=41162_203920",
+ "chars": [
+ "会",
+ "外",
+ "飲"
+ ]
+ },
+ {
+ "ja": "この町ではここがいちばん安い店です。",
+ "en": "This is the cheapest store in town.",
+ "expl": "此の{この} 町(まち) で(#2028980) は 此処{ここ} が 一番(いちばん)[01]{いちばん} 安い[01] 店(みせ) です",
+ "id": "ID=57866_220543",
+ "chars": [
+ "安",
+ "店",
+ "町"
+ ]
+ },
+ {
+ "ja": "ABC会社はまた赤字だ。",
+ "en": "The ABC company is in the red again.",
+ "expl": "会社 は 又{また} 赤字[01] だ",
+ "id": "ID=72268_234890",
+ "chars": [
+ "会",
+ "字",
+ "社",
+ "赤"
+ ]
+ },
+ {
+ "ja": "その音を聞くと犬は吠え出した。",
+ "en": "The noise set the dog barking.",
+ "expl": "其の[01]{その} 音[01] を 聞く と 犬[01] は 吠え出す{吠え出した}~",
+ "id": "ID=49434_212155",
+ "chars": [
+ "出",
+ "犬",
+ "聞",
+ "音"
+ ]
+ },
+ {
+ "ja": "その国は森林でおおわれている。",
+ "en": "The land is clothed with woods.",
+ "expl": "其の[01]{その} 国 は 森林~ で(#2028980) 覆う{おおわれている}",
+ "id": "ID=47860_210590",
+ "chars": [
+ "国",
+ "林",
+ "森"
+ ]
+ },
+ {
+ "ja": "なにも田中さんの力がオンリーだというのはない。",
+ "en": "This is not to say that Mr. Tanaka did it all on his own.",
+ "expl": "何も[01]{なにも} さん の 力(ちから) が オンリー だ と言うのは{というのは} 無い{ない}",
+ "id": "ID=327217_76503",
+ "chars": [
+ "中",
+ "力",
+ "田"
+ ]
+ },
+ {
+ "ja": "父は夕食の間中、一言もしゃべらなかった。",
+ "en": "My father didn't say a word during dinner.",
+ "expl": "父 は 夕食 の 間中 一言 も 喋る{しゃべらなかった}",
+ "id": "ID=319383_84332",
+ "chars": [
+ "一",
+ "中",
+ "夕",
+ "父",
+ "言",
+ "間",
+ "食"
+ ]
+ },
+ {
+ "ja": "名前を大文字で書いてください。",
+ "en": "Write your name in capitals.",
+ "expl": "名前 を 大文字[01] で(#2028980) 書く{書いて} 下さい{ください}",
+ "id": "ID=16886_80763",
+ "chars": [
+ "前",
+ "名",
+ "大",
+ "字",
+ "文",
+ "書"
+ ]
+ },
+ {
+ "ja": "何でこんなに早く来たの?",
+ "en": "What did you come here so early for?",
+ "expl": "何で[01] こんなに 早く[01] 来る(くる)[01]{来た} の",
+ "id": "ID=16270_177420",
+ "chars": [
+ "何",
+ "早",
+ "来"
+ ]
+ },
+ {
+ "ja": "村まで長い道のりだ。",
+ "en": "It is a long way to the village.",
+ "expl": "村 迄{まで} 長い 道程[01]{道のり} だ",
+ "id": "ID=274578_139153",
+ "chars": [
+ "村",
+ "道",
+ "長"
+ ]
+ }
+ ]
+ },
+ {
+ "level": "N3-1 + N3-2",
+ "chars": [
+ "兄",
+ "光",
+ "切",
+ "合",
+ "売",
+ "夏",
+ "家",
+ "広",
+ "弟",
+ "心",
+ "明",
+ "朝",
+ "楽",
+ "正",
+ "知",
+ "自",
+ "親",
+ "計",
+ "青",
+ "風"
+ ],
+ "chars_p1": [
+ "森",
+ "音",
+ "駅"
+ ],
+ "chars_p2": [
+ "会",
+ "分",
+ "母",
+ "毎",
+ "言"
+ ],
+ "chars_bad": [],
+ "examples": [
+ {
+ "ja": "あなたが言ったことはおおむね正しかった。",
+ "en": "What you said was in the main right.",
+ "expl": "貴方(あなた)[01]{あなた} が 言う{言った} 事(こと){こと} は 概ね{おおむね}~ 正しい{正しかった}",
+ "id": "ID=71462_234088",
+ "chars": [
+ "正",
+ "言"
+ ]
+ },
+ {
+ "ja": "毎朝、駅の時計で自分の時計を合わせます。",
+ "en": "Every morning I set my watch by the station clock.",
+ "expl": "毎朝 駅 の 時計 で(#2028980) 自分[01] の 時計 を 合わせる[01]{合わせます}",
+ "id": "ID=322370_81345",
+ "chars": [
+ "分",
+ "合",
+ "時",
+ "朝",
+ "毎",
+ "自",
+ "計",
+ "駅"
+ ]
+ },
+ {
+ "ja": "その兄弟は二人とも音楽家です。",
+ "en": "Both brothers are musicians.",
+ "expl": "其の[01]{その} 兄弟 は 二人とも{二人とも} 音楽家 です",
+ "id": "ID=48583_211309",
+ "chars": [
+ "二",
+ "人",
+ "兄",
+ "家",
+ "弟",
+ "楽",
+ "音"
+ ]
+ },
+ {
+ "ja": "どこへ行っても、親切で心の広い人々に出会いますよ。",
+ "en": "Wherever you go, you will meet people who are kind and generous.",
+ "expl": "何処{どこ} へ 行く[01]{行って} も 親切 で(#2028980) 心の広い~ 人々(ひとびと) に 出会う{出会います} よ[01]",
+ "id": "ID=38086_200882",
+ "chars": [
+ "人",
+ "会",
+ "出",
+ "切",
+ "広",
+ "心",
+ "行",
+ "親"
+ ]
+ },
+ {
+ "ja": "スイスは風光明媚なことでよく知られている。",
+ "en": "Switzerland is famous for its scenic beauty.",
+ "expl": "瑞西[01]{スイス} は 風光明媚{風光明媚な}~ 事(こと){こと} で(#2028980) 良く[01]{よく} 知る{知られている}",
+ "id": "ID=52438_215142",
+ "chars": [
+ "光",
+ "明",
+ "知",
+ "風"
+ ]
+ },
+ {
+ "ja": "ケンは今年の夏青森まで行った。",
+ "en": "Ken went as far as Aomori this summer.",
+ "expl": "は 今年 の 夏(なつ) 迄{まで} 行く[01]{行った}",
+ "id": "ID=62476_225140",
+ "chars": [
+ "今",
+ "夏",
+ "年",
+ "森",
+ "行",
+ "青"
+ ]
+ },
+ {
+ "ja": "母は自分の大切なものをすべて売ってしまった。",
+ "en": "My mother has sold everything that is dear to her.",
+ "expl": "母(はは) は 自分[01] の 大切(たいせつ){大切な} 物(もの){もの} を 全て{すべて} 売る{売って} 仕舞う{しまった}",
+ "id": "ID=320791_82925",
+ "chars": [
+ "分",
+ "切",
+ "売",
+ "大",
+ "母",
+ "自"
+ ]
+ }
+ ]
+ },
+ {
+ "level": "N3-2",
+ "chars": [
+ "京",
+ "作",
+ "冬",
+ "台",
+ "同",
+ "地",
+ "場",
+ "工",
+ "引",
+ "強",
+ "思",
+ "方",
+ "春",
+ "歌",
+ "海",
+ "用",
+ "画",
+ "紙",
+ "遠",
+ "鳥"
+ ],
+ "chars_p1": [
+ "広",
+ "明",
+ "朝",
+ "計",
+ "風"
+ ],
+ "chars_p2": [
+ "早",
+ "東"
+ ],
+ "chars_bad": [],
+ "examples": [
+ {
+ "ja": "その台風はその地方に多くの雨をもたらした。",
+ "en": "The typhoon brought lots of rain to that area.",
+ "expl": "其の[01]{その} 台風 は 其の[01]{その} 地方 に 多く の 雨 を 齎す[02]{もたらした}",
+ "id": "ID=45523_208266",
+ "chars": [
+ "台",
+ "地",
+ "多",
+ "方",
+ "雨",
+ "風"
+ ]
+ },
+ {
+ "ja": "上海の人口は、東京の人口と同じくらいです。",
+ "en": "The population of Shanghai is as large as that of Tokyo.",
+ "expl": "上海 の 人口[01] は 東京 の 人口[01] と 同じくらい です",
+ "id": "ID=268296_146266",
+ "chars": [
+ "上",
+ "京",
+ "人",
+ "口",
+ "同",
+ "東",
+ "海"
+ ]
+ },
+ {
+ "ja": "父はシェイクスピアを引用した手紙を書いていた。",
+ "en": "My father used to write letters that quoted from Shakespeare.",
+ "expl": "父 は を 引用 為る(する){した} 手紙 を 書く{書いていた}",
+ "id": "ID=318988_84726",
+ "chars": [
+ "引",
+ "手",
+ "書",
+ "父",
+ "用",
+ "紙"
+ ]
+ },
+ {
+ "ja": "そこに新しい工場を作ったらどうでしょうか。",
+ "en": "I suggest that we should build a new factory there.",
+ "expl": "其処(そこ){そこ} に 新しい 工場 を 作る{作ったら} 如何(どう){どう} でしょうか",
+ "id": "ID=50939_213652",
+ "chars": [
+ "作",
+ "場",
+ "工",
+ "新"
+ ]
+ },
+ {
+ "ja": "冬来たりなば、春遠からじ。",
+ "en": "If winter comes, can spring be far behind?",
+ "expl": "冬来たりなば春遠からじ{冬来たりなば、春遠からじ}~",
+ "id": "ID=279658_124336",
+ "chars": [
+ "冬",
+ "春",
+ "来",
+ "遠"
+ ]
+ },
+ {
+ "ja": "計画がこれほど早く明るみに出るとは思わなかった。",
+ "en": "We didn't expect an unveiling of the plan this soon.",
+ "expl": "計画 が 此れ程{これほど} 早く[01] 明るみに出る とは 思う{思わなかった}",
+ "id": "ID=238018_176446",
+ "chars": [
+ "出",
+ "思",
+ "早",
+ "明",
+ "画",
+ "計"
+ ]
+ },
+ {
+ "ja": "朝早くに、鳥が歌う。",
+ "en": "Birds sing early in the morning.",
+ "expl": "朝早く~ に 鳥[01] が 歌う[01]",
+ "id": "ID=277826_126166",
+ "chars": [
+ "早",
+ "朝",
+ "歌",
+ "鳥"
+ ]
+ },
+ {
+ "ja": "火は強風にあおられて四方に広がった。",
+ "en": "Fanned by the strong wind, the flames spread in all directions.",
+ "expl": "火(ひ) は 強風 に 煽る{あおられて} 四方 に 広がる{広がった}",
+ "id": "ID=23873_186737",
+ "chars": [
+ "四",
+ "広",
+ "強",
+ "方",
+ "火",
+ "風"
+ ]
+ }
+ ]
+ },
+ {
+ "level": "N3-2",
+ "chars": [
+ "元",
+ "回",
+ "図",
+ "夜",
+ "妹",
+ "姉",
+ "弱",
+ "教",
+ "昼",
+ "止",
+ "歩",
+ "池",
+ "理",
+ "色",
+ "茶",
+ "近",
+ "通",
+ "門",
+ "頭",
+ "顔"
+ ],
+ "chars_p1": [
+ "同",
+ "地"
+ ],
+ "chars_p2": [
+ "広",
+ "朝"
+ ],
+ "chars_bad": [],
+ "examples": [
+ {
+ "ja": "今日は昼と夜の長さが同じです。",
+ "en": "The lengths of day and night are the same today.",
+ "expl": "今日 は 昼[02] と 夜 の 長い{長} さ[01] が 同じ[01] です",
+ "id": "ID=242931_171544",
+ "chars": [
+ "今",
+ "同",
+ "夜",
+ "日",
+ "昼",
+ "長"
+ ]
+ },
+ {
+ "ja": "この地図で教えていただけますか。",
+ "en": "Will you show me on this map, please?",
+ "expl": "此の(この){この} 地図 で(#2028980) 教える{教えて} 頂ける[01]{いただけます} か",
+ "id": "ID=277226_126864",
+ "chars": [
+ "図",
+ "地",
+ "教"
+ ]
+ },
+ {
+ "ja": "地理は弱い。",
+ "en": "I am weak in geography.",
+ "expl": "地理 は 弱い",
+ "id": "ID=277270_126820",
+ "chars": [
+ "地",
+ "弱",
+ "理"
+ ]
+ },
+ {
+ "ja": "門はその車が通れるほど広い。",
+ "en": "The gate is wide enough for the car to go through.",
+ "expl": "門(もん)[01] は 其の[01]{その} 車(くるま) が 通る{通れる} 程{ほど} 広い",
+ "id": "ID=323930_79787",
+ "chars": [
+ "広",
+ "車",
+ "通",
+ "門"
+ ]
+ },
+ {
+ "ja": "朝は茶色トーストがいいです。",
+ "en": "I like brown toast in the morning.",
+ "expl": "朝(あさ) は 茶色 トースト が いい(#2820690)[01] です",
+ "id": "ID=277767_126226",
+ "chars": [
+ "朝",
+ "色",
+ "茶"
+ ]
+ },
+ {
+ "ja": "妹さんは元気?",
+ "en": "How's your sister?",
+ "expl": "妹さん は 元気",
+ "id": "ID=322300_81415",
+ "chars": [
+ "元",
+ "妹",
+ "気"
+ ]
+ },
+ {
+ "ja": "顔をべろべろ舐めるな。わはは。止めろよ。",
+ "en": "Don't slobber over my face! A-ha-ha-ha. Stop it!",
+ "expl": "顔(かお) を べろべろ舐める~ な わはは~ 止める(やめる){止めろ} よ[01]",
+ "id": "ID=327463_76257",
+ "chars": [
+ "止",
+ "顔"
+ ]
+ },
+ {
+ "ja": "姉はすごく頭がいい。",
+ "en": "My sister has a very good brain.",
+ "expl": "姉(あね) は 凄く{すごく} 頭がいい~",
+ "id": "ID=245571_237438",
+ "chars": [
+ "姉",
+ "頭"
+ ]
+ },
+ {
+ "ja": "あてもなくあちこちを歩き回った。",
+ "en": "I walked aimlessly about the street.",
+ "expl": "当ても無く{あてもなく}~ 彼方此方[01]{あちこち} を 歩き回る{歩き回った}",
+ "id": "ID=71701_234325",
+ "chars": [
+ "回",
+ "歩"
+ ]
+ },
+ {
+ "ja": "あの池には近づかないでください。",
+ "en": "Keep away from that pond, please.",
+ "expl": "彼の(あの){あの} 池 には 近づく{近づかないで} 下さい{ください}",
+ "id": "ID=67946_230579",
+ "chars": [
+ "池",
+ "近"
+ ]
+ }
+ ]
+ },
+ {
+ "level": "N3-2 + N3-3",
+ "chars": [
+ "体",
+ "声",
+ "太",
+ "室",
+ "市",
+ "帰",
+ "持",
+ "曜",
+ "牛",
+ "物",
+ "秋",
+ "答",
+ "考",
+ "肉",
+ "走",
+ "都",
+ "重",
+ "野",
+ "首",
+ "黒"
+ ],
+ "chars_p1": [
+ "図",
+ "夜",
+ "教",
+ "歩",
+ "通"
+ ],
+ "chars_p2": [
+ "京",
+ "地",
+ "引",
+ "思"
+ ],
+ "chars_bad": [],
+ "examples": [
+ {
+ "ja": "このなぞの答えを考えつきますか。",
+ "en": "Can you guess this riddle?",
+ "expl": "此の{この} 謎[01]{なぞの}~ 答え(#1449530) を 考えつく{考えつきます}~ か",
+ "id": "ID=60751_223417",
+ "chars": [
+ "答",
+ "考"
+ ]
+ },
+ {
+ "ja": "この牛肉は1ポンドにつき4ドルです。",
+ "en": "This beef is four dollars per pound.",
+ "expl": "此の{この} 牛肉 は ポンド に付き{につき} 弗[01]{ドル} です",
+ "id": "ID=59705_222379",
+ "chars": [
+ "牛",
+ "肉"
+ ]
+ },
+ {
+ "ja": "市は月曜ごとに立つ。",
+ "en": "The market is held every Monday.",
+ "expl": "市(いち)~ は 月曜 毎に{ごとに} 立つ[15]~",
+ "id": "ID=246123_168369",
+ "chars": [
+ "市",
+ "曜",
+ "月",
+ "立"
+ ]
+ },
+ {
+ "ja": "夜になって、家へ帰った。",
+ "en": "When night came on, we returned home.",
+ "expl": "夜 になる{になって} 家(いえ)[01] へ 帰る[01]{帰った}",
+ "id": "ID=323970_79747",
+ "chars": [
+ "夜",
+ "家",
+ "帰"
+ ]
+ },
+ {
+ "ja": "教室で食べるの?",
+ "en": "You eat in the classroom?",
+ "expl": "教室 で(#2028980) 食べる の",
+ "id": "ID=500370_500369",
+ "chars": [
+ "室",
+ "教",
+ "食"
+ ]
+ },
+ {
+ "ja": "「その通りだよ」と小さい黒いウサギは言いました。",
+ "en": "\"I really do,\" replied the little black rabbit.",
+ "expl": "その通り[01] だ よ と 小さい 黒い[01] 兎{ウサギ} は 言う{言いました}",
+ "id": "ID=73870_236536",
+ "chars": [
+ "小",
+ "言",
+ "通",
+ "黒"
+ ]
+ },
+ {
+ "ja": "ケリーの声は、よく通る。",
+ "en": "Kelly's voice carries well.",
+ "expl": "の 声 は 良く[01]{よく} 通る[06]~",
+ "id": "ID=62565_225230",
+ "chars": [
+ "声",
+ "通"
+ ]
+ },
+ {
+ "ja": "秋の夜長は読書にまさるものはありません。",
+ "en": "There's nothing better than reading in the long autumn nights.",
+ "expl": "秋(あき) の 夜長~ は 読書 に 勝る{まさる} 物(もの){もの} は 有る{ありません}",
+ "id": "ID=266359_148200",
+ "chars": [
+ "夜",
+ "書",
+ "秋",
+ "読",
+ "長"
+ ]
+ },
+ {
+ "ja": "走るな、ゆっくり歩け。",
+ "en": "Don't run, walk slowly.",
+ "expl": "走る{走るな} ゆっくり 歩く{歩け}",
+ "id": "ID=274291_140282",
+ "chars": [
+ "歩",
+ "走"
+ ]
+ },
+ {
+ "ja": "太っている人はみんなダイエットすべきだと思う。",
+ "en": "I think all fat people should go on a diet.",
+ "expl": "太る{太っている} 人(ひと) は 皆{みんな} ダイエット[01] す可き{すべき} だ と 思う",
+ "id": "ID=275012_138223",
+ "chars": [
+ "人",
+ "太",
+ "思"
+ ]
+ },
+ {
+ "ja": "木の下にいくつかの野生のキノコを見つけた。",
+ "en": "I found some wild mushrooms under the log.",
+ "expl": "木(き) の 下(した) に 幾つか{いくつかの} 野生{野生の} 茸{キノコ}~ を 見つける{見つけた}",
+ "id": "ID=323588_80129",
+ "chars": [
+ "下",
+ "木",
+ "生",
+ "見",
+ "野"
+ ]
+ },
+ {
+ "ja": "あなたの首は風前のともし火だ。",
+ "en": "Your job hangs by a thread.",
+ "expl": "貴方(あなた)[01]{あなた} の 首(くび)[03]~ は 風前の灯{風前のともし火}~ だ",
+ "id": "ID=70575_233205",
+ "chars": [
+ "前",
+ "火",
+ "風",
+ "首"
+ ]
+ },
+ {
+ "ja": "引力によって物体が重さを持つようになる。",
+ "en": "Gravity causes objects to have weight.",
+ "expl": "引力 に因って{によって} 物体(ぶったい) が 重い{重} さ[01] を 持つ 様になる{ようになる}",
+ "id": "ID=27132_189976",
+ "chars": [
+ "体",
+ "力",
+ "引",
+ "持",
+ "物",
+ "重"
+ ]
+ },
+ {
+ "ja": "京都市の地図を持っていますか。",
+ "en": "Do you have a map of the city of Kyoto?",
+ "expl": "NI{京都市} の 地図 を 持つ{持っています} か",
+ "id": "ID=19285_181533",
+ "chars": [
+ "京",
+ "図",
+ "地",
+ "市",
+ "持",
+ "都"
+ ]
+ }
+ ]
+ }
+] \ No newline at end of file
diff --git a/data/kanji_levels.txt b/data/kanji_levels.txt
index da163bc..ecd00ed 100644
--- a/data/kanji_levels.txt
+++ b/data/kanji_levels.txt
@@ -1,25 +1,25 @@
-4.1: 一右雨円下火花学気休金九空月見五口校左三山四子耳七車手十出女小上人水生先千川足大男中天土二日入年白八百本名木目立六
-4.2: 何会外間魚言古午後語行高国今時社週書少食新西前多長店電東道読南買半父分聞母北毎万友来話
-4.3: 安飲駅
-3.1: 音犬字森正青赤早村町田文夕力林
-3.2: 引遠夏家歌画回海楽顔帰牛京強教近兄計元光工広考合黒作姉市思止紙自室弱首秋春場色心親図声切走太体台知地池茶昼朝鳥通弟冬答頭同肉売風歩方妹明門夜野曜用理
-3.3: 悪暗意医員院運屋界開寒漢館起急究去業銀区軽研県仕使始死事持写者主終習集住重所暑乗真進世送族待代題短着注転都度動発病品服物勉味問薬有洋旅
-3.4: 以英建験好菜産試借説低働特飯不別便民料
-3.5: 質貸堂
-3.6: 映私洗
-2.1: 王貝玉糸石草竹虫
-2.2: 羽雲園黄科絵角活丸岩記形原戸交公才細算寺数星晴雪線船組谷直点当内馬麦番米鳴毛
-2.3: 委育泳央横温化荷階感岸期客球級橋局曲苦具君係決血庫湖向幸港号根祭坂皿指歯次式実取守酒受州拾宿助勝商消章植深申神身整昔全想相息速他打対第炭談柱調追定庭鉄登島投湯等童農波配倍箱畑反板悲皮美鼻筆氷表秒負部福平返放命面役油由遊予様葉陽落流両緑礼列練路和
-2.4: 愛案位衣印栄塩億加果課貨改械害各覚完官管観関願器希機季議求泣給漁競共協極訓群軍景芸欠結健固候康香差最材昨刷察札参散残司児治辞失種周祝順初焼照省笑城信臣成清静席積折節戦浅選然争側束続卒孫帯達単置仲兆底的伝徒努灯熱念敗飛必標付夫富府副兵変辺包法望末満未無約勇要浴利陸良量輪類令例冷連労老録
-2.5: 圧囲易移因営永液演応仮価可河過解快格確額刊慣喜基寄規技逆久救旧居許境均禁型経件検険減現限個故効厚構耕航講鉱告混査再妻採際在罪財殺雑賛史師志支枝資似示識授修術述準招象賞常情条状職制勢性政精製税績責接設絶祖総像増造則測損団断築貯張停程適導銅得毒独任燃能破判版犯比費非備評貧婦布武復複仏粉編保報豊暴貿防務夢迷綿輸余容率略留領歴
-2.6: 異胃域宇延灰拡革割巻干看簡危机疑吸供胸勤敬警劇券権呼誤紅降刻骨困砂座済冊詞誌捨若収純処署諸除将承蒸針専泉善層操窓装臓蔵存尊退宅担探暖段値宙著庁頂賃痛展党届難乳認脳拝背晩否批腹並閉片補暮宝訪亡忘棒枚優郵預幼欲翌乱卵裏律論
-2.8: 依偉違鋭越煙汚奥押欧菓介皆較乾換汗環甘含祈喫詰巨叫恐挟況狭偶隅掘靴傾恵迎肩賢軒枯雇互御更硬肯荒郊腰込婚歳咲伺刺脂湿舟柔緒召床昇紹畳触伸寝辛震吹姓占双捜掃燥憎贈替袋濯恥遅畜駐超沈珍泥滴殿塗渡途怒倒凍塔盗筒到逃突曇鈍軟猫悩濃杯泊薄爆肌髪抜般販彼疲被匹怖普浮符膚舞封幅払沸壁捕募抱坊帽忙磨埋眠娘戻与溶踊頼絡粒了涼療涙零齢恋湾腕
-1.2: 汽弓刀矢里
-1.3: 宮詩昭丁帳笛豆羊
-1.4: 井沖芽賀街潟岐旗挙鏡熊郡径功佐崎氏滋鹿唱松倉巣隊典徳奈縄梅博媛票牧養梨
-1.5: 衛益往幹眼紀義興句潔護災桜酸士飼舎謝序証織素属態提統肥弁墓脈
-1.6: 遺沿恩我閣株揮貴郷筋系激穴憲絹厳源己后孝皇鋼穀裁策蚕姿至視磁射尺樹宗就衆従縦縮熟傷障仁垂推寸盛聖誠舌宣染銭創奏誕忠潮腸敵糖討納派俳肺班秘俵奮陛幕密盟模訳覧臨朗
-1.8: 亜哀握扱威尉慰為維緯壱逸稲芋姻陰隠韻渦唄浦影詠疫悦謁閲宴援炎猿縁艶鉛凹旺殴翁憶乙卸穏佳嫁寡暇架禍稼箇華蚊雅餓塊壊怪悔懐戒拐劾慨概涯該垣嚇核殻獲穫郭隔岳掛喝括渇滑褐轄且鎌刈冠勘勧喚堪寛患憾敢棺款歓監緩缶肝艦貫還鑑閑陥頑企伎奇幾忌既棋棄軌輝飢騎鬼亀偽儀宜戯擬欺犠菊吉却脚虐丘及朽窮糾拒拠虚距享凶峡恭狂矯脅響驚仰凝暁錦斤琴緊菌襟謹吟駆駒愚虞遇屈繰桑勲薫刑啓契慶憩掲携渓継茎蛍鶏鯨撃傑倹兼剣圏堅嫌懸拳献謙遣顕幻弦玄孤弧虎誇顧鼓呉娯悟碁侯坑孔巧恒慌抗拘控攻江洪溝甲稿絞綱衡貢購酵項剛拷豪克酷獄墾恨懇昆紺魂唆沙詐鎖債催宰彩栽采砕斎載剤削搾索錯撮擦傘惨桟暫嗣施旨祉紫肢諮賜雌侍慈璽軸執漆疾芝赦斜煮遮蛇邪爵酌釈寂朱殊狩珠趣儒寿需囚愁秀臭襲酬醜充汁渋獣銃叔淑粛塾俊瞬准循旬殉潤盾巡遵庶叙徐償匠升奨宵尚彰抄掌晶沼渉焦症硝礁祥称粧肖衝訟詔詳鐘丈冗剰壌嬢浄譲醸錠嘱飾殖辱侵唇娠審慎振浸紳薪診刃尋甚尽迅陣須酢帥炊睡粋衰遂酔随髄崇枢据杉澄瀬畝是征牲誓請逝斉隻惜斥析籍跡拙摂窃仙扇栓潜旋繊薦践遷鮮漸禅繕塑措疎礎租粗訴阻僧喪壮爽挿曹槽荘葬藻遭霜騒促即俗賊汰堕妥惰駄耐怠泰滞胎逮滝卓択拓沢託濁諾但奪脱棚丹嘆旦淡端胆鍛壇弾痴稚致蓄逐秩窒嫡抽衷鋳弔彫徴懲挑眺聴跳勅朕鎮陳津墜椎塚漬坪釣鶴亭偵貞呈堤帝廷抵締艇訂逓邸摘哲徹撤迭添吐斗奴唐悼搭桃棟痘藤謄踏透陶騰闘憧洞瞳胴峠匿督篤凸屯豚那尼弐虹如尿妊忍寧粘把覇婆廃排輩培媒賠陪伯拍舶迫漠縛鉢伐罰閥伴帆搬畔繁藩範煩頒盤蛮卑妃扉披泌碑罷避尾微眉姫漂描苗浜賓頻敏瓶扶敷腐譜賦赴附侮伏覆噴墳憤紛雰丙併塀幣弊柄癖偏遍舗穂慕簿倣俸奉峰崩泡砲縫胞芳褒邦飽乏傍剖妨房某冒紡肪膨謀僕墨撲朴睦没堀奔翻凡盆摩魔麻膜又抹繭慢漫魅岬妙矛霧婿銘滅免茂妄猛盲網耗黙紋冶弥厄躍柳愉癒諭唯幽悠憂湧猶裕誘雄融誉庸揚揺擁窯謡抑翼羅裸雷酪嵐欄濫藍吏履璃痢離硫隆竜慮虜僚寮猟瞭糧陵倫厘隣瑠塁累励鈴隷霊麗暦劣烈裂廉錬呂炉露廊楼浪漏郎賄惑枠
-1.9: 阿葵茜渥旭梓絢綾鮎杏伊惟亥郁磯允胤卯丑叡瑛苑於伽嘉茄霞魁凱馨叶樺茅侃莞巌嬉毅稀誼鞠橘亨匡喬尭桐欣欽芹衿玖矩栗袈圭慧桂絃胡伍吾梧瑚鯉倖宏弘昂晃浩紘鴻嵯瑳裟哉冴朔笹皐燦爾蒔汐偲紗勺洲峻竣舜駿淳醇曙渚恕庄捷昌梢菖蕉丞穣晋榛秦翠錘瑞嵩雛碩銑惣綜聡蒼黛鯛鷹啄琢只辰巽檀智猪暢脹蝶槻蔦椿紬悌汀禎杜寅酉惇敦凪捺楠乃之巴萩肇鳩隼斐緋柊彦彪彬芙楓蕗碧甫輔朋萌鳳鵬槙柾亦麿巳稔椋孟匁也耶靖佑宥柚祐邑楊耀蓉遥蘭李琉亮凌稜諒遼琳麟伶嶺怜玲蓮禄倭亘侑勁奎崚彗昴晏晨晟暉栞椰毬洸洵滉漱澪燎燿瑶皓眸笙綺綸翔脩茉莉菫詢諄赳迪頌颯黎凜熙
-0.4: 茨岡阪埼栃阜
-0.8: 挨宛闇椅畏萎咽淫臼餌怨臆俺苛牙崖蓋骸柿顎葛釜瓦韓玩畿僅巾串窟稽詣隙桁鍵舷股乞勾喉梗頃痕挫塞柵拶斬嫉腫呪蹴拭尻芯腎裾凄醒戚脊煎羨腺詮膳曽狙遡痩捉袖遜唾堆戴誰綻酎捗潰爪諦溺貼妬賭頓謎鍋匂捻罵箸斑氾汎膝肘蔽蔑蜂貌勃昧枕蜜冥麺餅妖沃侶賂弄麓脇丼傲刹哺喩嗅嘲毀彙恣惧慄憬拉摯曖楷鬱璧瘍箋籠緻羞訃諧貪踪辣錮塡頰𠮟剝
+N4-1: 一右雨円下火花学気休金九空月見五口校左三山四子耳七車手十出女小上人水生先千川足大男中天土二日入年白八百本名木目立六
+N4-2: 何会外間魚言古午後語行高国今時社週書少食新西前多長店電東道読南買半父分聞母北毎万友来話
+N4-3: 安飲駅
+N3-1: 音犬字森正青赤早村町田文夕力林
+N3-2: 引遠夏家歌画回海楽顔帰牛京強教近兄計元光工広考合黒作姉市思止紙自室弱首秋春場色心親図声切走太体台知地池茶昼朝鳥通弟冬答頭同肉売風歩方妹明門夜野曜用理
+N3-3: 悪暗意医員院運屋界開寒漢館起急究去業銀区軽研県仕使始死事持写者主終習集住重所暑乗真進世送族待代題短着注転都度動発病品服物勉味問薬有洋旅
+N3-4: 以英建験好菜産試借説低働特飯不別便民料
+N3-5: 質貸堂
+N3-6: 映私洗
+N2-1: 王貝玉糸石草竹虫
+N2-2: 羽雲園黄科絵角活丸岩記形原戸交公才細算寺数星晴雪線船組谷直点当内馬麦番米鳴毛
+N2-3: 委育泳央横温化荷階感岸期客球級橋局曲苦具君係決血庫湖向幸港号根祭坂皿指歯次式実取守酒受州拾宿助勝商消章植深申神身整昔全想相息速他打対第炭談柱調追定庭鉄登島投湯等童農波配倍箱畑反板悲皮美鼻筆氷表秒負部福平返放命面役油由遊予様葉陽落流両緑礼列練路和
+N2-4: 愛案位衣印栄塩億加果課貨改械害各覚完官管観関願器希機季議求泣給漁競共協極訓群軍景芸欠結健固候康香差最材昨刷察札参散残司児治辞失種周祝順初焼照省笑城信臣成清静席積折節戦浅選然争側束続卒孫帯達単置仲兆底的伝徒努灯熱念敗飛必標付夫富府副兵変辺包法望末満未無約勇要浴利陸良量輪類令例冷連労老録
+N2-5: 圧囲易移因営永液演応仮価可河過解快格確額刊慣喜基寄規技逆久救旧居許境均禁型経件検険減現限個故効厚構耕航講鉱告混査再妻採際在罪財殺雑賛史師志支枝資似示識授修術述準招象賞常情条状職制勢性政精製税績責接設絶祖総像増造則測損団断築貯張停程適導銅得毒独任燃能破判版犯比費非備評貧婦布武復複仏粉編保報豊暴貿防務夢迷綿輸余容率略留領歴
+N2-6: 異胃域宇延灰拡革割巻干看簡危机疑吸供胸勤敬警劇券権呼誤紅降刻骨困砂座済冊詞誌捨若収純処署諸除将承蒸針専泉善層操窓装臓蔵存尊退宅担探暖段値宙著庁頂賃痛展党届難乳認脳拝背晩否批腹並閉片補暮宝訪亡忘棒枚優郵預幼欲翌乱卵裏律論
+N2-8: 依偉違鋭越煙汚奥押欧菓介皆較乾換汗環甘含祈喫詰巨叫恐挟況狭偶隅掘靴傾恵迎肩賢軒枯雇互御更硬肯荒郊腰込婚歳咲伺刺脂湿舟柔緒召床昇紹畳触伸寝辛震吹姓占双捜掃燥憎贈替袋濯恥遅畜駐超沈珍泥滴殿塗渡途怒倒凍塔盗筒到逃突曇鈍軟猫悩濃杯泊薄爆肌髪抜般販彼疲被匹怖普浮符膚舞封幅払沸壁捕募抱坊帽忙磨埋眠娘戻与溶踊頼絡粒了涼療涙零齢恋湾腕
+N1-2: 汽弓刀矢里
+N1-3: 宮詩昭丁帳笛豆羊
+N1-4: 井沖芽賀街潟岐旗挙鏡熊郡径功佐崎氏滋鹿唱松倉巣隊典徳奈縄梅博媛票牧養梨
+N1-5: 衛益往幹眼紀義興句潔護災桜酸士飼舎謝序証織素属態提統肥弁墓脈
+N1-6: 遺沿恩我閣株揮貴郷筋系激穴憲絹厳源己后孝皇鋼穀裁策蚕姿至視磁射尺樹宗就衆従縦縮熟傷障仁垂推寸盛聖誠舌宣染銭創奏誕忠潮腸敵糖討納派俳肺班秘俵奮陛幕密盟模訳覧臨朗
+N1-8: 亜哀握扱威尉慰為維緯壱逸稲芋姻陰隠韻渦唄浦影詠疫悦謁閲宴援炎猿縁艶鉛凹旺殴翁憶乙卸穏佳嫁寡暇架禍稼箇華蚊雅餓塊壊怪悔懐戒拐劾慨概涯該垣嚇核殻獲穫郭隔岳掛喝括渇滑褐轄且鎌刈冠勘勧喚堪寛患憾敢棺款歓監緩缶肝艦貫還鑑閑陥頑企伎奇幾忌既棋棄軌輝飢騎鬼亀偽儀宜戯擬欺犠菊吉却脚虐丘及朽窮糾拒拠虚距享凶峡恭狂矯脅響驚仰凝暁錦斤琴緊菌襟謹吟駆駒愚虞遇屈繰桑勲薫刑啓契慶憩掲携渓継茎蛍鶏鯨撃傑倹兼剣圏堅嫌懸拳献謙遣顕幻弦玄孤弧虎誇顧鼓呉娯悟碁侯坑孔巧恒慌抗拘控攻江洪溝甲稿絞綱衡貢購酵項剛拷豪克酷獄墾恨懇昆紺魂唆沙詐鎖債催宰彩栽采砕斎載剤削搾索錯撮擦傘惨桟暫嗣施旨祉紫肢諮賜雌侍慈璽軸執漆疾芝赦斜煮遮蛇邪爵酌釈寂朱殊狩珠趣儒寿需囚愁秀臭襲酬醜充汁渋獣銃叔淑粛塾俊瞬准循旬殉潤盾巡遵庶叙徐償匠升奨宵尚彰抄掌晶沼渉焦症硝礁祥称粧肖衝訟詔詳鐘丈冗剰壌嬢浄譲醸錠嘱飾殖辱侵唇娠審慎振浸紳薪診刃尋甚尽迅陣須酢帥炊睡粋衰遂酔随髄崇枢据杉澄瀬畝是征牲誓請逝斉隻惜斥析籍跡拙摂窃仙扇栓潜旋繊薦践遷鮮漸禅繕塑措疎礎租粗訴阻僧喪壮爽挿曹槽荘葬藻遭霜騒促即俗賊汰堕妥惰駄耐怠泰滞胎逮滝卓択拓沢託濁諾但奪脱棚丹嘆旦淡端胆鍛壇弾痴稚致蓄逐秩窒嫡抽衷鋳弔彫徴懲挑眺聴跳勅朕鎮陳津墜椎塚漬坪釣鶴亭偵貞呈堤帝廷抵締艇訂逓邸摘哲徹撤迭添吐斗奴唐悼搭桃棟痘藤謄踏透陶騰闘憧洞瞳胴峠匿督篤凸屯豚那尼弐虹如尿妊忍寧粘把覇婆廃排輩培媒賠陪伯拍舶迫漠縛鉢伐罰閥伴帆搬畔繁藩範煩頒盤蛮卑妃扉披泌碑罷避尾微眉姫漂描苗浜賓頻敏瓶扶敷腐譜賦赴附侮伏覆噴墳憤紛雰丙併塀幣弊柄癖偏遍舗穂慕簿倣俸奉峰崩泡砲縫胞芳褒邦飽乏傍剖妨房某冒紡肪膨謀僕墨撲朴睦没堀奔翻凡盆摩魔麻膜又抹繭慢漫魅岬妙矛霧婿銘滅免茂妄猛盲網耗黙紋冶弥厄躍柳愉癒諭唯幽悠憂湧猶裕誘雄融誉庸揚揺擁窯謡抑翼羅裸雷酪嵐欄濫藍吏履璃痢離硫隆竜慮虜僚寮猟瞭糧陵倫厘隣瑠塁累励鈴隷霊麗暦劣烈裂廉錬呂炉露廊楼浪漏郎賄惑枠
+N1-9: 阿葵茜渥旭梓絢綾鮎杏伊惟亥郁磯允胤卯丑叡瑛苑於伽嘉茄霞魁凱馨叶樺茅侃莞巌嬉毅稀誼鞠橘亨匡喬尭桐欣欽芹衿玖矩栗袈圭慧桂絃胡伍吾梧瑚鯉倖宏弘昂晃浩紘鴻嵯瑳裟哉冴朔笹皐燦爾蒔汐偲紗勺洲峻竣舜駿淳醇曙渚恕庄捷昌梢菖蕉丞穣晋榛秦翠錘瑞嵩雛碩銑惣綜聡蒼黛鯛鷹啄琢只辰巽檀智猪暢脹蝶槻蔦椿紬悌汀禎杜寅酉惇敦凪捺楠乃之巴萩肇鳩隼斐緋柊彦彪彬芙楓蕗碧甫輔朋萌鳳鵬槙柾亦麿巳稔椋孟匁也耶靖佑宥柚祐邑楊耀蓉遥蘭李琉亮凌稜諒遼琳麟伶嶺怜玲蓮禄倭亘侑勁奎崚彗昴晏晨晟暉栞椰毬洸洵滉漱澪燎燿瑶皓眸笙綺綸翔脩茉莉菫詢諄赳迪頌颯黎凜熙
+N0-4: 茨岡阪埼栃阜
+N0-8: 挨宛闇椅畏萎咽淫臼餌怨臆俺苛牙崖蓋骸柿顎葛釜瓦韓玩畿僅巾串窟稽詣隙桁鍵舷股乞勾喉梗頃痕挫塞柵拶斬嫉腫呪蹴拭尻芯腎裾凄醒戚脊煎羨腺詮膳曽狙遡痩捉袖遜唾堆戴誰綻酎捗潰爪諦溺貼妬賭頓謎鍋匂捻罵箸斑氾汎膝肘蔽蔑蜂貌勃昧枕蜜冥麺餅妖沃侶賂弄麓脇丼傲刹哺喩嗅嘲毀彙恣惧慄憬拉摯曖楷鬱璧瘍箋籠緻羞訃諧貪踪辣錮塡頰𠮟剝
diff --git a/html/style.css b/html/style.css
new file mode 100644
index 0000000..f4e6e3c
--- /dev/null
+++ b/html/style.css
@@ -0,0 +1,24 @@
+a {
+ color: blue;
+}
+
+.ja {
+ text-align: center;
+ font-size: 2em;
+}
+
+.ja:hover .char_cur {
+ color: red;
+}
+
+.ja:hover .char_p1 {
+ color: magenta;
+}
+
+.ja:hover .char_p2 {
+ color: blue;
+}
+
+.ja:hover .char_bad {
+ color: grey;
+}
diff --git a/src/main.rs b/src/main.rs
index d1efece..6c67680 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,9 +1,11 @@
use std::collections::HashMap;
use std::fs;
use std::cmp::Ordering;
-use std::io::{self, BufRead};
+use std::io::{self, BufRead, Write};
use anyhow::{anyhow, Result};
+use rayon::prelude::*;
+use serde::{Serialize, Deserialize};
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
@@ -17,6 +19,7 @@ struct Opt {
enum Cmd {
ParseKanjidic,
New,
+ Format,
}
fn main() {
@@ -28,7 +31,7 @@ fn main() {
for (jlpt, grade, chars) in levels.iter() {
println!("{}.{}: {}", jlpt, grade, chars);
}
- },
+ }
Cmd::New => {
let kanji_levels = read_kanji_levels().expect("read_kanji_levels");
let all_kanji = Charset::new(kanji_levels.iter()
@@ -38,12 +41,27 @@ fn main() {
let kanji_levels = kanji_levels.into_iter()
.map(|(l, x)| (l, Charset::new(x)))
.collect::<Vec<_>>();
- let ex = read_examples(&all_kanji).expect("read_examples");
- println!("{:#?}", ex.iter().take(10).collect::<Vec<_>>());
- let batch1 = gen_batch(&[], &kanji_levels, &ex).expect("gen_batch");
- println!("{:#?}", batch1);
- let batch2 = gen_batch(&[batch1], &kanji_levels, &ex).expect("gen_batch");
- println!("{:#?}", batch2);
+ let mut ex = read_examples(&all_kanji).expect("read_examples");
+ ex.retain(|e| (5..=25).contains(&e.ja.chars().count()));
+ let mut batches: Vec<Batch> = fs::read("data/batches.json")
+ .map_err(anyhow::Error::from)
+ .and_then(|x| Ok(serde_json::from_slice(&x)?))
+ .unwrap_or_default();
+ println!("---- starting after {} batches ----", batches.len());
+ for _ in 0..10 {
+ let batch = gen_batch(&batches, &kanji_levels, &ex).expect("gen_batch");
+ batches.push(batch);
+ }
+ fs::write("data/batches.json", serde_json::to_string_pretty(&batches).expect("serialize").as_bytes()).expect("save");
+ }
+ Cmd::Format => {
+ let batches = fs::read("data/batches.json")
+ .map_err(anyhow::Error::from)
+ .and_then(|x| Ok(serde_json::from_slice::<Vec<Batch>>(&x)?))
+ .expect("read/parse");
+ batches.par_iter()
+ .enumerate()
+ .for_each(|x| format_batch(batches.len(), x));
}
}
}
@@ -153,52 +171,142 @@ fn gen_batch(previous: &[Batch], kanji_levels: &[(String, Charset)], examples: &
.find(|(_, _, c)| !c.is_empty())
.ok_or(anyhow!("no more batches to make!"))?;
- let chars_4 = kanji_levels[..target_i].iter().rev().next()
- .map(|(_, c)| c.clone()).unwrap_or(Charset::new(""));
+ let chars_p1 = previous.iter().rev().next()
+ .map(|b| b.chars.clone()).unwrap_or(Charset::default());
- let chars_2 = kanji_levels[..target_i].iter().rev().skip(1).next()
- .map(|(_, c)| c.clone()).unwrap_or(Charset::new(""));
+ let chars_p2 = previous.iter().rev().skip(1).next()
+ .map(|b| b.chars.clone()).unwrap_or(Charset::default());
- let chars_bad = Charset::from_iter(kanji_levels.iter().skip(target_i+1)
+ let mut chars_missing = Charset::default();
+
+ let mut chars_bad = Charset::from_iter(kanji_levels.iter().skip(target_i+1)
.map(|(_, c)| c.chars().iter().copied())
.flatten());
let mut batch = Batch {
level: target_level.to_string(),
- chars: Charset::new(""),
+ chars: Charset::default(),
+ chars_p1: Charset::default(),
+ chars_p2: Charset::default(),
+ chars_bad: Charset::default(),
examples: Vec::new(),
};
+ let mut batch_chars = Charset::default();
- eprintln!("Target (val=10) : {}", target_chars.to_string());
- eprintln!("Prev1 (val=4) : {}", chars_4.to_string());
- eprintln!("Prev2 (val=2) : {}", chars_2.to_string());
- eprintln!("Bad (val=-10): {}", chars_bad.to_string());
+ eprintln!("----");
+ eprintln!("Level : {}", batch.level);
+ eprintln!("Target : {}", target_chars.to_string());
+ eprintln!("Prev1 : {}", chars_p1.to_string());
+ eprintln!("Prev2 : {}", chars_p2.to_string());
+ eprintln!("Bad : {} characters", chars_bad.len());
let batch_len = 20;
while batch.chars.len() < batch_len && !target_chars.is_empty() {
- if let Some((ex, _)) = examples.iter()
+ let need = batch_len - batch.chars.len();
+ if need >= 2 && target_chars.len() <= 1 && target_i + 1 < kanji_levels.len() {
+ // upgrade to next level
+ target_i += 1;
+ chars_missing = chars_missing.union(&target_chars);
+ target_chars = target_chars.union(&kanji_levels[target_i].1);
+ chars_bad = chars_bad.diff(&target_chars);
+ if batch.examples.is_empty() {
+ batch.level = kanji_levels[target_i].0.to_string();
+ } else {
+ batch.level = format!("{} + {}", batch.level, kanji_levels[target_i].0);
+ }
+ eprintln!("Level : {}", batch.level);
+ eprintln!("Target: {}", target_chars.to_string());
+ eprintln!("Missing: {}", chars_missing.to_string());
+ eprintln!("Bad : {} characters", chars_bad.len());
+ }
+ if let Some((ex, _)) = examples.par_iter()
.map(|ex| (ex, ex.chars.inter_len(&target_chars)))
- .filter(|(_, ex_tgt_inter)| *ex_tgt_inter <= 4)
- .filter(|(_, ex_tgt_inter)| *ex_tgt_inter + batch.chars.len() <= batch_len + 1)
+ .filter(|(_, ex_tgt_inter)| (1..=4).contains(ex_tgt_inter) && *ex_tgt_inter + batch.chars.len() <= batch_len)
.max_by_key(|(ex, ex_tgt_inter)|
- 10i32 * *ex_tgt_inter as i32
- + 4i32 * ex.chars.inter_len(&chars_4) as i32
- + 2i32 * ex.chars.inter_len(&chars_2) as i32
+ 20i32 * *ex_tgt_inter as i32
+ + 30i32 * ex.chars.inter_len(&chars_missing) as i32
+ + 6i32 * ex.chars.inter_len(&batch.chars) as i32
+ + 4i32 * ex.chars.inter_len(&chars_p1) as i32
+ + 3i32 * ex.chars.inter_len(&chars_p2) as i32
- 40i32 * ex.chars.inter_len(&chars_bad) as i32) {
- println!("add: {:?} (bad: {})", ex, ex.chars.inter(&chars_bad).to_string());
+ eprintln!("* add {} (rep: {}, p1: {}, p2: {}, bad: {}) {}",
+ ex.chars.inter(&target_chars).to_string(),
+ ex.chars.inter(&batch.chars).to_string(),
+ ex.chars.inter(&chars_p1).to_string(),
+ ex.chars.inter(&chars_p2).to_string(),
+ ex.chars.inter(&chars_bad).to_string(),
+ ex.ja);
batch.chars = batch.chars.union(&ex.chars.inter(&target_chars));
target_chars = target_chars.diff(&ex.chars);
+ chars_missing = chars_missing.diff(&ex.chars);
batch.examples.push(ex.clone());
+ batch_chars = batch_chars.union(&ex.chars);
} else {
- eprintln!("could not find sentence that doesn't add myriads too many characters, stopping batch now");
+ eprintln!("could not find suitable sentence, stopping batch now (missing {})", need);
break;
}
}
+ batch.chars_p1 = chars_p1.inter(&batch_chars);
+ batch.chars_p2 = chars_p2.inter(&batch_chars);
+ batch.chars_bad = chars_bad.inter(&batch_chars);
+
Ok(batch)
}
-#[derive(Debug, Clone)]
+fn format_batch(count: usize, (i, batch): (usize, &Batch)) {
+ format_batch_aux(count, i, batch).expect("format batch");
+}
+
+fn format_batch_aux(count: usize, i: usize, batch: &Batch) -> Result<()> {
+ let mut f = io::BufWriter::new(fs::File::create(format!("html/{:03}.html", i))?);
+ write!(f, r#"<!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset=\"UTF-8\" />
+ <title>Batch #{:03}</title>
+ <link rel="stylesheet" type="text/css" href="style.css" />
+ </head>
+ <body>"#, i)?;
+
+ writeln!(f, "<p>")?;
+ for j in 0..count {
+ if j != i {
+ writeln!(f, r#" <a href="{:03}.html">{:03}</a>"#, j, j)?;
+ } else {
+ writeln!(f, " {:03}", j)?;
+ }
+ }
+ writeln!(f, r#"</p>"#)?;
+ writeln!(f, "<p>Level: {}</p>", batch.level)?;
+ writeln!(f, "<p>Characters: {}</p>", batch.chars.to_string())?;
+
+ for ex in batch.examples.iter() {
+ writeln!(f, "<hr />")?;
+ write!(f, r#"<p class="ja">"#)?;
+ for c in ex.ja.chars() {
+ if batch.chars.contains(c) {
+ write!(f, r#"<span class="char_cur">{}</span>"#, c)?;
+ } else if batch.chars_p1.contains(c) {
+ write!(f, r#"<span class="char_p1">{}</span>"#, c)?;
+ } else if batch.chars_p2.contains(c) {
+ write!(f, r#"<span class="char_p2">{}</span>"#, c)?;
+ } else if batch.chars_bad.contains(c) {
+ write!(f, r#"<span class="char_bad">{}</span>"#, c)?;
+ } else {
+ write!(f, "{}", c)?;
+ }
+ }
+ writeln!(f, "</p>")?;
+ writeln!(f, r#"<p style="text-align: center; font-size: 1.2em">{}</p>"#, ex.en)?;
+ }
+
+ write!(f, "</body></html>")?;
+ f.flush()?;
+ Ok(())
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
struct Example {
ja: String,
en: String,
@@ -207,14 +315,17 @@ struct Example {
chars: Charset,
}
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Serialize, Deserialize)]
struct Batch {
level: String,
chars: Charset,
+ chars_p1: Charset,
+ chars_p2: Charset,
+ chars_bad: Charset,
examples: Vec<Example>,
}
-#[derive(Debug, Eq, PartialEq, Hash, Clone)]
+#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, Default)]
struct Charset(Vec<char>);
impl Charset {