Skip to content

Commit b101664

Browse files
committed
shiritori: update dependencies
- bump all dependencies version - add missing fields to package.json - adapt to kuroshiro breaking change - switch to Promise for dispatching result
1 parent df40267 commit b101664

File tree

9 files changed

+5690
-3709
lines changed

9 files changed

+5690
-3709
lines changed

functions/index.js

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,14 @@ const { actionssdk, SimpleResponse } = require('actions-on-google');
2323
const functions = require('firebase-functions');
2424
const shiritori = require('./shiritori');
2525
const admin = require('firebase-admin');
26-
admin.initializeApp(functions.config().firebase);
26+
admin.initializeApp();
2727

2828
const corpus = 'noun';
2929

30-
const dict = k => {
31-
return admin.database()
30+
const dict = k => admin.database()
3231
.ref(corpus).child(k)
3332
.once('value')
3433
.then(snap => snap.val());
35-
};
3634

3735
const app = actionssdk({
3836
// リクエストとレスポンスをロギングする。
@@ -45,35 +43,30 @@ const app = actionssdk({
4543
})
4644
});
4745

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

52-
app.intent('actions.intent.TEXT', (conv, input) => {
53-
return shiritori.loaded.then(() => {
54-
return shiritori.interact(dict, input, conv.data.used)
55-
.then(result => {
56-
conv.data.used.unshift(input);
57-
conv.data.used.unshift(result.word);
58-
conv.ask(new SimpleResponse({
59-
speech: result.word,
60-
text: `${result.word} [${result.kana}]`
61-
}));
62-
})
63-
.catch(result => {
64-
if (result.win) {
65-
if (result.word) {
66-
conv.close(`${result.word} [${result.kana}]`);
67-
} else {
68-
conv.close('すごい!あなたの勝ちです。');
69-
}
70-
} else if (result.loose) {
71-
conv.close('ざんねん。あなたの負けです。');
72-
} else {
73-
throw result;
74-
}
75-
});
76-
});
77-
});
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+
}));
7871

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

functions/package.json

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
11
{
22
"name": "shiritori-agent",
3+
"version": "0.0.2",
34
"description": "shiritori agent fullfillment",
4-
"version": "0.0.1",
5-
"private": true,
6-
"license": "Apache Version 2.0",
75
"author": "Google Inc.",
6+
"license": "Apache License 2.0",
7+
"repository": {
8+
"type": "git",
9+
"url": "https://github.com/actions-on-google/actionssdk-shiritori-ja-nodejs"
10+
},
11+
"private": true,
812
"engines": {
9-
"node": "~6.0"
13+
"node": "8"
1014
},
1115
"scripts": {
1216
"lint": "eslint --fix \"**/*.js\"",
1317
"start": "firebase serve --only functions",
1418
"deploy": "firebase deploy --only functions:shiritoriV3"
1519
},
1620
"dependencies": {
17-
"actions-on-google": "^2.1.x",
18-
"firebase-admin": "^5.12.1",
19-
"firebase-functions": "^1.0.3",
20-
"kuroshiro": "^0.2.1",
21-
"object.entries": "^1.0.4",
21+
"actions-on-google": "^2.6.0",
22+
"firebase-admin": "^7.2.0",
23+
"firebase-functions": "^2.2.1",
24+
"kuroshiro": "^1.1.2",
25+
"object.entries": "^1.1.0",
2226
"shiritori": "file:shiritori",
23-
"wanakana": "^3.1.0"
27+
"wanakana": "^4.0.2"
2428
},
2529
"devDependencies": {
2630
"eslint": "^3.19.0",

functions/shiritori/index.js

Lines changed: 81 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -14,116 +14,104 @@
1414

1515
'use strict'
1616

17-
const kuroshiro = require('kuroshiro')
17+
const Kuroshiro = require('kuroshiro')
18+
const KuromojiAnalyzer = require('kuroshiro-analyzer-kuromoji')
1819
const wanakana = require('wanakana')
1920

2021
const smallKanas = 'ぁぃぅぇぉゃゅょ'
21-
let kuroshiroLoaded = false
2222

2323
// 辞書のローディング。
24-
exports.loaded = new Promise((resolve, reject) => {
25-
kuroshiro.init(function (err) {
26-
if (err) {
27-
reject(err)
28-
return
29-
}
30-
kuroshiroLoaded = true
31-
resolve()
32-
})
33-
})
34-
35-
// しりとりのルールのチェック。
36-
exports.check = (word, chain) => {
37-
if ((chain === undefined) || (chain.length === 0)) {
38-
return true
39-
}
40-
41-
if (kuroshiroLoaded) {
42-
word = kuroshiro.toHiragana(word)
43-
chain = chain.map((e) => kuroshiro.toHiragana(e))
44-
}
24+
const kuroshiro = new Kuroshiro()
25+
const loaded = kuroshiro.init(new KuromojiAnalyzer())
4526

46-
// 漢字からひらがなにする。
47-
const wordHira = wanakana.toHiragana(word)
48-
const chainHira = chain.map(wanakana.toHiragana)
27+
class Win extends Error {}
28+
exports.Win = Win
29+
class Bad extends Error {}
30+
exports.Bad = Bad
4931

50-
// 使った名詞をチェックする。
51-
if (chainHira.indexOf(wordHira, 0) !== -1) {
52-
return false
53-
}
32+
// しりとりのルールのチェック。
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+
}
5443

55-
// しりとりの最初の文字をチェックする。
56-
const validKanas = exports.kanas(chain[0])
57-
for (const k of validKanas) {
58-
const begin = wordHira.slice(0, k.length)
59-
if (begin === k) {
44+
if ((chain === undefined) || (chain.length === 0)) {
6045
return true
6146
}
62-
}
63-
return false
64-
}
6547

66-
// 名詞からしりとりのひらがなを選ぶ
67-
exports.kanas = (word) => new Set((() => {
68-
if (kuroshiroLoaded) {
69-
word = kuroshiro.toHiragana(word)
70-
}
48+
// 使った名詞をチェックする
49+
const chainHira = chain.map(wanakana.toHiragana)
50+
if (chainHira.indexOf(wordHira, 0) !== -1) {
51+
throw new Bad('already used')
52+
}
7153

72-
const wordKana = wanakana.toKatakana(word)
73-
const wordHira = wanakana.toHiragana(wordKana)
74-
const kk = wordKana[word.length - 1]
75-
const hk = wordHira[wordHira.length - 1]
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+
})
65+
})
7666

77-
if (hk === 'ん') {
78-
return []
79-
}
67+
// 名詞からしりとりのひらがなを選ぶ。
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+
}
8079

81-
if ((kk === 'ー') && (wordKana.length > 1)) {
82-
return [
83-
wordHira[wordHira.length - 1],
84-
wordHira[wordHira.length - 2]
85-
]
86-
}
80+
if ((kk === 'ー') && (wordKana.length > 1)) {
81+
return [
82+
wordHira[wordHira.length - 1],
83+
wordHira[wordHira.length - 2]
84+
]
85+
}
8786

88-
if (smallKanas.includes(hk)) {
87+
if (smallKanas.includes(hk)) {
88+
return [
89+
String.fromCharCode(hk.charCodeAt(0) + 1),
90+
wordHira.slice(-2)
91+
]
92+
}
8993
return [
90-
String.fromCharCode(hk.charCodeAt(0) + 1),
91-
wordHira.slice(-2)
94+
wordHira[wordHira.length - 1]
9295
]
93-
}
94-
95-
return [
96-
wordHira[wordHira.length - 1]
97-
]
98-
})())
96+
})
97+
.then(result => new Set(result))
9998

10099
// しりとりのゲームループ。
101-
exports.interact = (dict, word, chain) => new Promise((resolve, reject) => {
102-
const next = exports.kanas(word)
103-
if ((next.size === 0) || !exports.check(word, chain)) {
104-
return reject({loose: true})
105-
}
106-
const key = next.values().next().value
107-
dict(key).then((words) => {
108-
const unused = []
109-
if (words) {
110-
for (const k of Object.keys(words)) {
111-
if (!chain.includes(words[k])) {
112-
unused.push(k)
113-
}
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')
114107
}
115-
}
116-
if (unused.length === 0) {
117-
return reject({win: true})
118-
}
119-
const w = unused[Math.floor(Math.random() * unused.length)]
120-
const wk = words[w]
121-
if (wk.length === 0) {
122-
return reject({error: 'empty dictionary entry for key: ' + w})
123-
}
124-
if (wk[wk.length - 1] === 'ん') {
125-
return reject({win: true, word: w, kana: wk})
126-
}
127-
resolve({word: w, kana: wk})
128-
})
129-
})
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+
})

functions/shiritori/main.js

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,30 +30,28 @@ const rl = readline.createInterface({
3030
const chain = ['しりとり']
3131
console.log(chain[0])
3232
rl.prompt()
33-
shiritori.loaded.then(() => {
34-
rl.on('line', input => {
35-
shiritori
36-
.interact(
37-
kana => Promise.resolve(corpus[kana]),
38-
input,
39-
chain
40-
)
41-
.then(result => {
42-
console.log(`${result.word} [${result.kana}]`)
43-
chain.unshift(input)
44-
chain.unshift(result.kana)
45-
rl.prompt()
46-
})
47-
.catch(reason => {
48-
if (reason.win) {
49-
console.log('すごい!')
50-
process.exit(0)
51-
} else if (reason.loose) {
52-
console.log('ざんねん。')
53-
process.exit(-1)
54-
} else {
55-
throw reason
56-
}
57-
})
33+
rl.on('line', input => {
34+
shiritori.interact(
35+
kana => Promise.resolve(corpus[kana]),
36+
input,
37+
chain
38+
).then(result => {
39+
console.log(`${result.word} [${result.kana}]`)
40+
chain.unshift(input)
41+
chain.unshift(result.kana)
42+
rl.prompt()
43+
}).catch(reason => {
44+
switch (reason.constructor) {
45+
case shiritori.Win:
46+
console.log('すごい!')
47+
process.exit(0)
48+
break
49+
case shiritori.Bad:
50+
console.log('ざんねん。')
51+
process.exit(-1)
52+
break
53+
default:
54+
throw reason
55+
}
5856
})
5957
})

0 commit comments

Comments
 (0)