11import { type Letter } from "@keybr/phonetic-model" ;
2- import { type KeyStats , type KeyStatsMap , speedToTime } from "@keybr/result" ;
2+ import {
3+ type KeySample ,
4+ type KeyStats ,
5+ type KeyStatsMap ,
6+ speedToTime ,
7+ } from "@keybr/result" ;
38import { type Settings } from "@keybr/settings" ;
49import { type LessonKeys } from "./key.ts" ;
510import { Target } from "./target.ts" ;
611
12+ const defaultSampleCount = 5 ;
13+
714export function fakeKeyStatsMap (
815 settings : Settings ,
916 items : [
1017 letter : Letter ,
1118 confidence : number | null ,
1219 bestConfidence : number | null ,
20+ sampleCount ?: number ,
1321 ] [ ] ,
1422) : KeyStatsMap {
1523 const target = new Target ( settings ) ;
1624 const map = new Map < Letter , KeyStats > (
17- items . map ( ( [ letter , confidence , bestConfidence ] ) => {
25+ items . map ( ( [ letter , confidence , bestConfidence , sampleCount ] ) => {
1826 const timeToType =
1927 confidence == null
2028 ? null
@@ -23,28 +31,54 @@ export function fakeKeyStatsMap(
2331 bestConfidence == null
2432 ? null
2533 : speedToTime ( target . targetSpeed ) / bestConfidence ;
34+ const count =
35+ confidence == null && bestConfidence == null
36+ ? 0
37+ : ( sampleCount ?? defaultSampleCount ) ;
38+ const samples : KeySample [ ] = Array . from (
39+ { length : count } ,
40+ ( _ , index ) => ( {
41+ index,
42+ timeStamp : 0 ,
43+ hitCount : 1 ,
44+ missCount : 0 ,
45+ timeToType : timeToType ?? 0 ,
46+ filteredTimeToType : timeToType ?? 0 ,
47+ } ) ,
48+ ) ;
2649 return [
2750 letter ,
2851 {
2952 letter,
30- samples : [ ] ,
53+ samples,
3154 timeToType,
3255 bestTimeToType,
3356 } as KeyStats ,
3457 ] ;
3558 } ) ,
3659 ) ;
60+ // A real KeyStatsMap.results grows by one per round; GuidedLesson.update()
61+ // memoizes on it (plus identity of the last entry) to make its two
62+ // same-round call sites idempotent. Synthesize one placeholder entry per
63+ // sample so that varying sample counts across simulated rounds is seen
64+ // as new evidence, while passing the exact same fakeKeyStatsMap() result
65+ // to update() twice (same array, same last entry) is seen as a repeat.
66+ const totalSamples = [ ...map . values ( ) ] . reduce (
67+ ( sum , keyStats ) => sum + keyStats . samples . length ,
68+ 0 ,
69+ ) ;
70+ const results = Array . from ( { length : totalSamples } , ( ) => ( { } ) ) ;
3771 return {
3872 letters : [ ...map . keys ( ) ] ,
39- results : [ ] ,
73+ results,
4074 get : ( letter ) => map . get ( letter ) ,
4175 [ Symbol . iterator ] : ( ) => map . values ( ) ,
4276 } as KeyStatsMap ;
4377}
4478
4579export function printLessonKeys ( lessonKeys : LessonKeys ) {
46- return lessonKeys
47- . findIncludedKeys ( )
80+ return [ ... lessonKeys ]
81+ . filter ( ( key ) => key . isIncluded || key . isSecondFocused )
4882 . map ( ( key ) => {
4983 let s = `${ key . letter } ` ;
5084 if ( key . isForced ) {
@@ -53,6 +87,9 @@ export function printLessonKeys(lessonKeys: LessonKeys) {
5387 if ( key . isFocused ) {
5488 s = `[${ s } ]` ;
5589 }
90+ if ( key . isSecondFocused ) {
91+ s = `(${ s } )` ;
92+ }
5693 return s ;
5794 } )
5895 . join ( "" ) ;
0 commit comments