Skip to content

Commit 21f9067

Browse files
committed
add stats
1 parent 87aed2a commit 21f9067

File tree

9 files changed

+205
-119
lines changed

9 files changed

+205
-119
lines changed

examples/react-backspace/src/backspace.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export class BackspaceRequirdAutomaton {
1212
}
1313

1414
input(stroke: InputEvent): InputResult {
15-
const { result, acceptedEdge } = this.base.testInput(stroke);
15+
const [result, apply] = this.base.testInput(stroke);
1616
if (result.isIgnored) {
1717
return result;
1818
}
@@ -25,7 +25,7 @@ export class BackspaceRequirdAutomaton {
2525
this._failedInputs.push(stroke);
2626
}
2727
// 状態遷移する
28-
this.base.applyState(stroke, result, acceptedEdge);
28+
apply()
2929
return result;
3030
}
3131

examples/react-result-record/src/App.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import "./App.css";
22
import * as emiel from "emiel";
3-
import { useEffect, useState } from "react";
3+
import { useEffect, useMemo, useState } from "react";
44
import { Typing } from "./Typing";
55
import { Record, WordRecordValue } from "./Record";
66

@@ -9,34 +9,40 @@ function App() {
99
useEffect(() => {
1010
emiel.keyboard.detect(window).then(setLayout);
1111
}, []);
12-
const [isFinished, setIsFinished] = useState(false);
12+
const automatons = useMemo(() => {
13+
const words = ["おをひく", "こんとん", "がっこう", "aから@"];
14+
return words.map((w) => {
15+
return emiel.rule.getRoman(layout).build(w);
16+
})
17+
}, [layout])
18+
const [wordIndex, setWordIndex] = useState(0);
1319
const [wordRecords, setWordRecords] = useState<WordRecordValue[]>([]);
1420
const onWordFinished = (
1521
a: emiel.Automaton,
1622
displayedAt: Date,
17-
missCount: number
1823
) => {
19-
console.log("missCount: ", missCount);
2024
setWordRecords((wordRecords) => [
2125
...wordRecords,
2226
{
2327
automaton: a,
2428
displayedAt,
25-
firstInputtedAt: a.succeededInputs[0].event.timestamp,
29+
firstInputtedAt: a.histories[0].event.timestamp,
2630
finishedAt:
27-
a.succeededInputs[a.succeededInputs.length - 1].event.timestamp,
28-
missCount: missCount,
31+
a.histories[a.histories.length - 1].event.timestamp,
2932
},
3033
]);
34+
setWordIndex((current) => {
35+
return current + 1;
36+
});
3137
};
3238
return layout ? (
33-
isFinished ? (
39+
wordIndex >= automatons.length ? (
3440
<Record wordRecords={wordRecords} />
3541
) : (
3642
<Typing
3743
layout={layout}
44+
automaton={automatons[wordIndex]}
3845
onWordFinished={onWordFinished}
39-
onFinished={() => setIsFinished(true)}
4046
/>
4147
)
4248
) : (

examples/react-result-record/src/Record.tsx

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,22 @@ import * as emiel from "emiel";
33
export type WordRecordValue = {
44
automaton: emiel.Automaton;
55
displayedAt: Date;
6-
firstInputtedAt: Date;
7-
finishedAt: Date;
8-
missCount: number;
96
};
107

118
export function Record(props: { wordRecords: WordRecordValue[] }) {
129
const records = props.wordRecords.map((record) => {
1310
// ワードが表示されてから1打鍵めに成功するまでの経過時間
11+
const automaton = record.automaton;
1412
const latency =
15-
record.firstInputtedAt.getTime() - record.displayedAt.getTime();
16-
// latency の時間を除いた、1分あたりの打鍵数
17-
// 「latency の時間を除く」=「1打鍵めに要する時間を除く」ということなので、
18-
// 打鍵数を1減らして計算する
19-
const rkpm = Math.trunc(
20-
((record.automaton.succeededInputs.length - 1) /
21-
(record.finishedAt.getTime() - record.firstInputtedAt.getTime())) *
22-
1000 *
23-
60
24-
);
13+
automaton.firstInputTime.getTime() - record.displayedAt.getTime();
14+
const rkpm = emiel.stats.rkpm(
15+
automaton.histories.length,
16+
automaton.firstInputTime,
17+
automaton.lastInputTime);
18+
2519
// latency の時間を含めた、1分あたりの打鍵数
26-
const kpm = Math.trunc(
27-
(record.automaton.succeededInputs.length /
28-
(record.finishedAt.getTime() - record.displayedAt.getTime())) *
29-
1000 *
30-
60
31-
);
32-
const accuracy =
33-
record.automaton.succeededInputs.length /
34-
(record.missCount + record.automaton.succeededInputs.length);
20+
const kpm = emiel.stats.kpm(automaton.histories.length, record.displayedAt, automaton.lastInputTime);
21+
const accuracy = emiel.stats.accuracy(record.automaton.failedInputCount, record.automaton.totalInputCount);
3522
return {
3623
latency,
3724
kpm,
@@ -42,26 +29,27 @@ export function Record(props: { wordRecords: WordRecordValue[] }) {
4229
};
4330
});
4431
const totalLatency = records.reduce((acc, r) => acc + r.latency, 0);
45-
const totalSucceededKeys = records.reduce(
46-
(acc, r) => acc + r.record.automaton.succeededInputs.length,
32+
const totalSucceededCount = records.reduce(
33+
(acc, r) => acc + r.record.automaton.histories.length,
4734
0
4835
);
49-
const totalMissedKeys = records.reduce(
50-
(acc, r) => acc + r.record.missCount,
36+
const totalFailedCount = records.reduce(
37+
(acc, r) => acc + r.record.automaton.failedInputCount,
5138
0
5239
);
40+
const totalAccuracy = emiel.stats.accuracy(totalFailedCount, totalSucceededCount);
5341
return (
5442
<div>
5543
<h1>Finished!</h1>
5644
<div style={{ display: "flex", justifyContent: "center" }}>
5745
<table style={{ border: "0" }}>
5846
<tr>
5947
<td>inputs</td>
60-
<td>{totalSucceededKeys}</td>
48+
<td>{totalSucceededCount}</td>
6149
</tr>
6250
<tr>
6351
<td>miss</td>
64-
<td>{totalMissedKeys}</td>
52+
<td>{totalFailedCount}</td>
6553
</tr>
6654
<tr>
6755
<td>avg latency</td>
@@ -70,11 +58,7 @@ export function Record(props: { wordRecords: WordRecordValue[] }) {
7058
<tr>
7159
<td>accuracy</td>
7260
<td>
73-
{(
74-
(totalSucceededKeys / (totalSucceededKeys + totalMissedKeys)) *
75-
100
76-
).toFixed(2)}
77-
%
61+
{(totalAccuracy * 100).toFixed(2)} %
7862
</td>
7963
</tr>
8064
</table>

examples/react-result-record/src/Typing.tsx

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,28 @@ import "./App.css";
22
import * as emiel from "emiel";
33
import { useEffect, useState } from "react";
44

5-
const words = ["おをひく", "こんとん", "がっこう", "aから@"];
6-
75
export function Typing(props: {
86
layout: emiel.KeyboardLayout;
7+
automaton: emiel.Automaton;
98
onWordFinished: (
109
a: emiel.Automaton,
1110
displayedAt: Date,
12-
missCount: number
1311
) => void;
14-
onFinished: () => void;
1512
}) {
16-
const [automatons] = useState(
17-
words.map((w) => {
18-
return emiel.rule.getRoman(props.layout).build(w);
19-
})
20-
);
21-
const [wordIndex, setWordIndex] = useState(0);
22-
const [wordDisplayedAt, setWordDisplayedAt] = useState(new Date());
23-
const [missCount, setMissCount] = useState(0);
2413
const [lastInputKey, setLastInputKey] = useState<
2514
emiel.InputStroke | undefined
2615
>();
27-
const automaton = automatons[wordIndex];
16+
const automaton = props.automaton;
2817
useEffect(() => {
29-
setWordDisplayedAt(new Date());
18+
const wordDisplayedAt = new Date();
3019
return emiel.activate(window, (e) => {
31-
console.log("missCount in activate: ", missCount);
3220
setLastInputKey(e.input);
3321
const result = automaton.input(e);
3422
if (result.isFinished) {
35-
console.log("missCount in: ", missCount);
36-
props.onWordFinished(automaton, wordDisplayedAt, missCount);
37-
if (wordIndex === words.length - 1) {
38-
props.onFinished();
39-
}
40-
setWordIndex((current) => current + 1);
41-
setWordDisplayedAt(new Date());
42-
setMissCount(0);
43-
}
44-
if (result.isFailed) {
45-
setMissCount((current) => current + 1);
23+
props.onWordFinished(automaton, wordDisplayedAt);
4624
}
4725
});
48-
}, [wordIndex]);
26+
}, [automaton]);
4927
return (
5028
<>
5129
<h1>
@@ -59,7 +37,7 @@ export function Typing(props: {
5937
<h2>
6038
Miss:{" "}
6139
<code>
62-
{missCount}
40+
{automaton.failedInputCount}
6341
</code>
6442
</h2>
6543
<h2>

0 commit comments

Comments
 (0)