Skip to content

Commit 09e6bbf

Browse files
committed
shiritori: use async/await
1 parent b101664 commit 09e6bbf

File tree

3 files changed

+107
-97
lines changed

3 files changed

+107
-97
lines changed

functions/index.js

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -43,30 +43,32 @@ const app = actionssdk({
4343
})
4444
});
4545

46-
app.intent('actions.intent.MAIN',
47-
conv => conv.ask('どうぞ、始めて下さい'));
46+
app.intent('actions.intent.MAIN', conv => {
47+
conv.ask('どうぞ、始めて下さい');
48+
});
4849

49-
app.intent('actions.intent.TEXT',
50-
(conv, input) => shiritori.interact(dict, input, conv.data.used)
51-
.then(result => {
52-
conv.data.used.unshift(input);
53-
conv.data.used.unshift(result.word);
54-
conv.ask(new SimpleResponse({
55-
speech: result.word,
56-
text: `${result.word} [${result.kana}]`
57-
}));
58-
})
59-
.catch(reason => {
60-
switch (reason.constructor) {
61-
case shiritori.Win:
62-
conv.close('すごい!あなたの勝ちです。');
63-
break;
64-
case shiritori.Bad:
65-
conv.close('ざんねん。あなたの負けです。');
66-
break;
67-
default:
68-
throw reason;
69-
}
70-
}));
50+
app.intent('actions.intent.TEXT', (conv, input) => {
51+
return shiritori.interact(dict, input, conv.data.used)
52+
.then(result => {
53+
conv.data.used.unshift(input);
54+
conv.data.used.unshift(result.word);
55+
conv.ask(new SimpleResponse({
56+
speech: result.word,
57+
text: `${result.word} [${result.kana}]`
58+
}));
59+
})
60+
.catch(reason => {
61+
switch (reason.constructor) {
62+
case shiritori.Win:
63+
conv.close('すごい!あなたの勝ちです。');
64+
break;
65+
case shiritori.Bad:
66+
conv.close('ざんねん。あなたの負けです。');
67+
break;
68+
default:
69+
throw reason;
70+
}
71+
});
72+
});
7173

7274
exports.shiritoriV3 = functions.https.onRequest(app);

functions/shiritori/index.js

Lines changed: 79 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -29,89 +29,95 @@ exports.Win = Win
2929
class Bad extends Error {}
3030
exports.Bad = Bad
3131

32+
// https://github.com/hexenq/kuroshiro/issues/64
33+
async function toHiragana(word) {
34+
const hira = await kuroshiro.convert(word, { to: 'hiragana' })
35+
return wanakana.toHiragana(hira)
36+
}
37+
3238
// しりとりのルールのチェック。
33-
exports.check = (word, chain) => loaded.then(() => {
34-
return Promise.all([
35-
kuroshiro.convert(word, { to: 'hiragana' }),
36-
...chain.map(e => kuroshiro.convert(e, { to: 'hiragana' }))
37-
]).then(([word, ...chain]) => {
38-
// 漢字からひらがなにする。
39-
const wordHira = wanakana.toHiragana(word)
40-
if (wordHira[wordHira.length - 1] === 'ん') {
41-
throw new Bad('ends with ん')
42-
}
39+
exports.check = (word, chain) => loaded.then(async () => {
40+
//「ん」で終わるかどうか?
41+
const wordHira = await toHiragana(word)
42+
if (wordHira[wordHira.length - 1] === 'ん') {
43+
throw new Bad('ends with ん')
44+
}
4345

44-
if ((chain === undefined) || (chain.length === 0)) {
45-
return true
46-
}
46+
// 漢字からひらがなにする。
47+
if ((chain === undefined) || (chain.length === 0)) {
48+
return true
49+
}
4750

48-
// 使った名詞をチェックする。
49-
const chainHira = chain.map(wanakana.toHiragana)
50-
if (chainHira.indexOf(wordHira, 0) !== -1) {
51-
throw new Bad('already used')
52-
}
51+
// 使った名詞をチェックする。
52+
const chainHira = await Promise.all(chain.map(toHiragana))
53+
if (chainHira.includes(wordHira)) {
54+
throw new Bad('already used')
55+
}
5356

54-
// しりとりの最初の文字をチェックする。
55-
return exports.kanas(chain[0]).then(validKanas => {
56-
for (const k of validKanas) {
57-
const begin = wordHira.slice(0, k.length)
58-
if (begin === k) {
59-
return true
60-
}
61-
}
62-
throw new Bad('文字 does not match')
63-
})
64-
})
57+
// しりとりの最初の文字をチェックする。
58+
const validKanas = await exports.kanas(chain[0])
59+
for (const k of validKanas) {
60+
const begin = wordHira.slice(0, k.length)
61+
if (begin === k) {
62+
return true
63+
}
64+
}
65+
throw new Bad('文字 does not match')
6566
})
6667

6768
// 名詞からしりとりのひらがなを選ぶ。
68-
exports.kanas = word => loaded
69-
.then(() => kuroshiro.convert(word, { to: 'hiragana' }))
70-
.then((word) => {
71-
const wordKana = wanakana.toKatakana(word)
72-
const wordHira = wanakana.toHiragana(wordKana)
73-
const kk = wordKana[wordKana.length - 1]
74-
const hk = wordHira[wordHira.length - 1]
75-
76-
if (hk === 'ん') {
77-
return []
78-
}
69+
exports.kanas = word => loaded.then(async () => {
70+
const wordKana = await kuroshiro.convert(word, { to: 'katakana' })
71+
const wordHira = wanakana.toHiragana(wordKana)
72+
const kk = wordKana[wordKana.length - 1]
73+
const hk = wordHira[wordHira.length - 1]
7974

80-
if ((kk === 'ー') && (wordKana.length > 1)) {
81-
return [
82-
wordHira[wordHira.length - 1],
83-
wordHira[wordHira.length - 2]
84-
]
85-
}
75+
if (hk === 'ん') {
76+
return []
77+
}
8678

87-
if (smallKanas.includes(hk)) {
88-
return [
89-
String.fromCharCode(hk.charCodeAt(0) + 1),
90-
wordHira.slice(-2)
91-
]
92-
}
79+
// サー→「さ、あ」。
80+
if ((kk === 'ー') && (wordKana.length > 1)) {
9381
return [
94-
wordHira[wordHira.length - 1]
82+
wordHira[wordHira.length - 1],
83+
wordHira[wordHira.length - 2]
9584
]
96-
})
97-
.then(result => new Set(result))
85+
}
86+
87+
// しゃ→「しゃ、や」。
88+
if (smallKanas.includes(hk)) {
89+
return [
90+
String.fromCharCode(hk.charCodeAt(0) + 1),
91+
wordHira.slice(-2)
92+
]
93+
}
94+
95+
// 最後の文字。
96+
return [
97+
wordHira[wordHira.length - 1]
98+
]
99+
}).then(result => new Set(result))
98100

99101
// しりとりのゲームループ。
100-
exports.interact = (dict, word, chain) => exports.check(word, chain)
101-
.then(() => exports.kanas(word))
102-
.then(validKanas => dict(validKanas.values().next().value))
103-
.then(words => {
104-
const unused = words ? Object.keys(words).filter(k => !chain.includes(words[k])) : []
105-
if (unused.length === 0) {
106-
throw new Win('no more unused word remaining in dictionary')
107-
}
108-
const w = unused[Math.floor(Math.random() * unused.length)]
109-
const wk = words[w]
110-
if (wk.length === 0) {
111-
throw new Error(`no dictionary entry for key: ${w}`)
112-
}
113-
if (wk[wk.length - 1] === 'ん') {
114-
throw new Win('dictionary word ends with ん')
115-
}
116-
return { word: w, kana: wk }
117-
})
102+
exports.interact = (dict, word, chain) => {
103+
return exports.check(word, chain).then(async () => {
104+
const validKanas = await exports.kanas(word)
105+
// 他のかなもチェックしたほうかいい。
106+
const firstValid = validKanas.values().next().value
107+
const words = await dict(firstValid)
108+
const unused = words ? Object.keys(words).filter(k => !chain.includes(words[k])) : []
109+
if (unused.length === 0) {
110+
throw new Win('no more unused word remaining in dictionary')
111+
}
112+
// ランダムでえらぶ。
113+
const w = unused[Math.floor(Math.random() * unused.length)]
114+
const wk = words[w]
115+
if (wk.length === 0) {
116+
throw new Error(`no dictionary entry for key: ${w}`)
117+
}
118+
if (wk[wk.length - 1] === 'ん') {
119+
throw new Win('dictionary word ends with ん')
120+
}
121+
return { word: w, kana: wk }
122+
})
123+
}

functions/shiritori/test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ test('check: shiritori', async t => {
2222
test('check: ends with ん', async t => {
2323
await t.throwsAsync(shiritori.check('とん', ['べんと']),
2424
//)
25+
await t.throwsAsync(shiritori.check('トン', ['べんと']),
26+
//)
2527
})
2628

2729
test('check: already used', async t => {

0 commit comments

Comments
 (0)