@willwade/aac-processors 0.0.16 ā 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/utilities/analytics/metrics/comparison.d.ts +2 -1
- package/dist/utilities/analytics/metrics/comparison.js +141 -22
- package/dist/utilities/analytics/metrics/core.js +4 -8
- package/dist/utilities/analytics/metrics/effort.d.ts +15 -0
- package/dist/utilities/analytics/metrics/effort.js +21 -0
- package/dist/utilities/analytics/metrics/types.d.ts +22 -0
- package/dist/utilities/analytics/reference/data/base_words.en.json +11684 -0
- package/dist/utilities/analytics/reference/data/common_words.en.json +1585 -0
- package/dist/utilities/analytics/reference/data/core_lists.en.json +1608 -0
- package/dist/utilities/analytics/reference/data/fringe.en.json +4236 -0
- package/dist/utilities/analytics/reference/data/sentences.en.json +32 -0
- package/dist/utilities/analytics/reference/data/synonyms.en.json +69 -0
- package/dist/utilities/analytics/reference/index.d.ts +14 -0
- package/dist/utilities/analytics/reference/index.js +48 -2
- package/package.json +3 -2
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* analyze vocabulary differences, and generate CARE component scores.
|
|
6
6
|
*/
|
|
7
7
|
import { MetricsResult, ComparisonResult } from './types';
|
|
8
|
+
import { MetricsOptions } from './types';
|
|
8
9
|
export declare class ComparisonAnalyzer {
|
|
9
10
|
private vocabAnalyzer;
|
|
10
11
|
private sentenceAnalyzer;
|
|
@@ -17,7 +18,7 @@ export declare class ComparisonAnalyzer {
|
|
|
17
18
|
compare(targetResult: MetricsResult, compareResult: MetricsResult, options?: {
|
|
18
19
|
includeSentences?: boolean;
|
|
19
20
|
locale?: string;
|
|
20
|
-
}): ComparisonResult;
|
|
21
|
+
} & Partial<MetricsOptions>): ComparisonResult;
|
|
21
22
|
/**
|
|
22
23
|
* Calculate CARE component scores
|
|
23
24
|
*/
|
|
@@ -10,6 +10,7 @@ exports.ComparisonAnalyzer = void 0;
|
|
|
10
10
|
const sentence_1 = require("./sentence");
|
|
11
11
|
const vocabulary_1 = require("./vocabulary");
|
|
12
12
|
const index_1 = require("../reference/index");
|
|
13
|
+
const effort_1 = require("./effort");
|
|
13
14
|
class ComparisonAnalyzer {
|
|
14
15
|
constructor() {
|
|
15
16
|
this.vocabAnalyzer = new vocabulary_1.VocabularyAnalyzer();
|
|
@@ -79,7 +80,7 @@ class ComparisonAnalyzer {
|
|
|
79
80
|
};
|
|
80
81
|
});
|
|
81
82
|
// Calculate CARE components
|
|
82
|
-
const careComponents = this.calculateCareComponents(targetResult, compareResult, overlappingWords);
|
|
83
|
+
const careComponents = this.calculateCareComponents(targetResult, compareResult, overlappingWords, options);
|
|
83
84
|
// Analyze high/low effort words
|
|
84
85
|
const highEffortWords = [];
|
|
85
86
|
const lowEffortWords = [];
|
|
@@ -216,7 +217,42 @@ class ComparisonAnalyzer {
|
|
|
216
217
|
/**
|
|
217
218
|
* Calculate CARE component scores
|
|
218
219
|
*/
|
|
219
|
-
calculateCareComponents(targetResult, compareResult, _overlappingWords) {
|
|
220
|
+
calculateCareComponents(targetResult, compareResult, _overlappingWords, options) {
|
|
221
|
+
// Load common words with baseline efforts (matching Ruby line 527-534)
|
|
222
|
+
const commonWordsData = this.referenceLoader.loadCommonWords();
|
|
223
|
+
const commonWords = new Map();
|
|
224
|
+
commonWordsData.words.forEach((word) => {
|
|
225
|
+
commonWords.set(word.toLowerCase(), commonWordsData.efforts[word] || 0);
|
|
226
|
+
});
|
|
227
|
+
// Determine prediction settings (default: use common words efforts, not prediction)
|
|
228
|
+
const usePrediction = options?.usePrediction || false; // Default FALSE (use common words)
|
|
229
|
+
const predictionSelections = options?.predictionSelections || 1.5;
|
|
230
|
+
const debugMode = process.env.DEBUG_METRICS === 'true';
|
|
231
|
+
// Helper function to calculate fallback effort
|
|
232
|
+
const getFallbackEffort = (word, hasPrediction, spellingBaseEffort) => {
|
|
233
|
+
const wordLower = word.toLowerCase();
|
|
234
|
+
// Check common words efforts first (matching Ruby line 533)
|
|
235
|
+
if (commonWords.has(wordLower)) {
|
|
236
|
+
const effort = commonWords.get(wordLower);
|
|
237
|
+
return effort !== undefined ? effort : (0, effort_1.spellingEffort)(word, 10, 2.5);
|
|
238
|
+
}
|
|
239
|
+
// If usePrediction is true and prediction is available, use prediction
|
|
240
|
+
if (usePrediction && hasPrediction && spellingBaseEffort !== undefined) {
|
|
241
|
+
return (0, effort_1.predictionEffort)(spellingBaseEffort, 2.5, predictionSelections, 2);
|
|
242
|
+
}
|
|
243
|
+
// Fallback to manual spelling (matching Ruby spelling_effort: 10 + word.length * 2.5)
|
|
244
|
+
return (0, effort_1.spellingEffort)(word, 10, 2.5);
|
|
245
|
+
};
|
|
246
|
+
// Debug: Check settings
|
|
247
|
+
const targetHasPrediction = targetResult.has_dynamic_prediction && targetResult.spelling_effort_base !== undefined;
|
|
248
|
+
const _compareHasPrediction = compareResult.has_dynamic_prediction && compareResult.spelling_effort_base !== undefined;
|
|
249
|
+
if (debugMode) {
|
|
250
|
+
console.log(`\nš DEBUG Fallback Effort Settings:`);
|
|
251
|
+
console.log(` Common words loaded: ${commonWords.size}`);
|
|
252
|
+
console.log(` usePrediction option: ${usePrediction}`);
|
|
253
|
+
console.log(` Target has prediction capability: ${targetHasPrediction}`);
|
|
254
|
+
console.log(` Target spelling_base: ${targetResult.spelling_effort_base?.toFixed(2) || 'undefined'}`);
|
|
255
|
+
}
|
|
220
256
|
// Create word maps with normalized keys
|
|
221
257
|
const targetWords = new Map();
|
|
222
258
|
targetResult.buttons.forEach((btn) => {
|
|
@@ -237,62 +273,142 @@ class ComparisonAnalyzer {
|
|
|
237
273
|
// Load reference data
|
|
238
274
|
const coreLists = this.referenceLoader.loadCoreLists();
|
|
239
275
|
const fringe = this.referenceLoader.loadFringe();
|
|
276
|
+
const commonFringe = this.referenceLoader.loadCommonFringe();
|
|
240
277
|
const sentences = this.referenceLoader.loadSentences();
|
|
241
|
-
// Calculate core coverage
|
|
278
|
+
// Calculate core coverage and effort (matching Ruby lines 609-647)
|
|
242
279
|
let coreCount = 0;
|
|
243
280
|
let compCoreCount = 0;
|
|
281
|
+
let targetCoreEffort = 0;
|
|
282
|
+
let compCoreEffort = 0;
|
|
244
283
|
const allCoreWords = new Set();
|
|
245
284
|
coreLists.forEach((list) => {
|
|
246
285
|
list.words.forEach((word) => allCoreWords.add(word.toLowerCase()));
|
|
247
286
|
});
|
|
248
287
|
allCoreWords.forEach((word) => {
|
|
249
288
|
const key = this.normalize(word);
|
|
250
|
-
|
|
289
|
+
const targetBtn = targetWords.get(key);
|
|
290
|
+
const compareBtn = compareWords.get(key);
|
|
291
|
+
if (targetBtn) {
|
|
251
292
|
coreCount++;
|
|
252
|
-
|
|
293
|
+
targetCoreEffort += targetBtn.effort;
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
// Fallback to spelling or prediction effort
|
|
297
|
+
targetCoreEffort += getFallbackEffort(word, targetResult.has_dynamic_prediction || false, targetResult.spelling_effort_base);
|
|
298
|
+
}
|
|
299
|
+
if (compareBtn) {
|
|
253
300
|
compCoreCount++;
|
|
301
|
+
compCoreEffort += compareBtn.effort;
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
compCoreEffort += getFallbackEffort(word, compareResult.has_dynamic_prediction || false, compareResult.spelling_effort_base);
|
|
305
|
+
}
|
|
254
306
|
});
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
307
|
+
const avgCoreEffort = allCoreWords.size > 0 ? targetCoreEffort / allCoreWords.size : 0;
|
|
308
|
+
const avgCompCoreEffort = allCoreWords.size > 0 ? compCoreEffort / allCoreWords.size : 0;
|
|
309
|
+
// Calculate core component scores (matching Ruby lines 644-647)
|
|
310
|
+
const coreScore = avgCoreEffort * 5.0;
|
|
311
|
+
const compCoreScore = avgCompCoreEffort * 5.0;
|
|
312
|
+
// Calculate sentence construction effort (matching Ruby lines 654-668)
|
|
313
|
+
const sentenceEfforts = [];
|
|
314
|
+
const compSentenceEfforts = [];
|
|
259
315
|
sentences.forEach((words) => {
|
|
316
|
+
let targetSentenceEffort = 0;
|
|
317
|
+
let compSentenceEffort = 0;
|
|
260
318
|
words.forEach((word) => {
|
|
261
319
|
const key = this.normalize(word);
|
|
262
320
|
const targetBtn = targetWords.get(key);
|
|
263
321
|
const compareBtn = compareWords.get(key);
|
|
264
322
|
if (targetBtn) {
|
|
265
|
-
|
|
323
|
+
targetSentenceEffort += targetBtn.effort;
|
|
266
324
|
}
|
|
267
325
|
else {
|
|
268
|
-
|
|
326
|
+
targetSentenceEffort += getFallbackEffort(word, targetResult.has_dynamic_prediction || false, targetResult.spelling_effort_base);
|
|
269
327
|
}
|
|
270
328
|
if (compareBtn) {
|
|
271
329
|
compSentenceEffort += compareBtn.effort;
|
|
272
330
|
}
|
|
273
331
|
else {
|
|
274
|
-
compSentenceEffort +=
|
|
332
|
+
compSentenceEffort += getFallbackEffort(word, compareResult.has_dynamic_prediction || false, compareResult.spelling_effort_base);
|
|
275
333
|
}
|
|
276
|
-
sentenceWordCount++;
|
|
277
334
|
});
|
|
335
|
+
// Average effort per sentence (matching Ruby line 657)
|
|
336
|
+
sentenceEfforts.push(targetSentenceEffort / words.length);
|
|
337
|
+
compSentenceEfforts.push(compSentenceEffort / words.length);
|
|
278
338
|
});
|
|
279
|
-
const avgSentenceEffort =
|
|
280
|
-
|
|
281
|
-
|
|
339
|
+
const avgSentenceEffort = sentenceEfforts.length > 0
|
|
340
|
+
? sentenceEfforts.reduce((a, b) => a + b, 0) / sentenceEfforts.length
|
|
341
|
+
: 0;
|
|
342
|
+
const compAvgSentenceEffort = compSentenceEfforts.length > 0
|
|
343
|
+
? compSentenceEfforts.reduce((a, b) => a + b, 0) / compSentenceEfforts.length
|
|
344
|
+
: 0;
|
|
345
|
+
// Sentence component scores (matching Ruby line 665-668)
|
|
346
|
+
const sentenceScore = avgSentenceEffort * 3.0;
|
|
347
|
+
const compSentenceScore = compAvgSentenceEffort * 3.0;
|
|
348
|
+
// Calculate fringe effort (matching Ruby lines 670-687)
|
|
349
|
+
const fringeEfforts = [];
|
|
350
|
+
const compFringeEfforts = [];
|
|
282
351
|
let fringeCount = 0;
|
|
283
352
|
let compFringeCount = 0;
|
|
284
|
-
let commonFringeCount = 0;
|
|
285
353
|
fringe.forEach((word) => {
|
|
286
354
|
const key = this.normalize(word);
|
|
287
|
-
const
|
|
288
|
-
const
|
|
289
|
-
if (
|
|
355
|
+
const targetBtn = targetWords.get(key);
|
|
356
|
+
const compareBtn = compareWords.get(key);
|
|
357
|
+
if (targetBtn) {
|
|
358
|
+
fringeEfforts.push(targetBtn.effort);
|
|
290
359
|
fringeCount++;
|
|
291
|
-
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
fringeEfforts.push(getFallbackEffort(word, targetResult.has_dynamic_prediction || false, targetResult.spelling_effort_base));
|
|
363
|
+
}
|
|
364
|
+
if (compareBtn) {
|
|
365
|
+
compFringeEfforts.push(compareBtn.effort);
|
|
292
366
|
compFringeCount++;
|
|
293
|
-
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
compFringeEfforts.push(getFallbackEffort(word, compareResult.has_dynamic_prediction || false, compareResult.spelling_effort_base));
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
const avgFringeEffort = fringeEfforts.length > 0
|
|
373
|
+
? fringeEfforts.reduce((a, b) => a + b, 0) / fringeEfforts.length
|
|
374
|
+
: 0;
|
|
375
|
+
const avgCompFringeEffort = compFringeEfforts.length > 0
|
|
376
|
+
? compFringeEfforts.reduce((a, b) => a + b, 0) / compFringeEfforts.length
|
|
377
|
+
: 0;
|
|
378
|
+
// Fringe component scores (matching Ruby line 684-687)
|
|
379
|
+
const fringeScore = avgFringeEffort * 2.0;
|
|
380
|
+
const compFringeScore = avgCompFringeEffort * 2.0;
|
|
381
|
+
// Calculate common fringe effort (matching Ruby lines 689-705)
|
|
382
|
+
const commonFringeEfforts = [];
|
|
383
|
+
const compCommonFringeEfforts = [];
|
|
384
|
+
let commonFringeCount = 0;
|
|
385
|
+
commonFringe.forEach((word) => {
|
|
386
|
+
const key = this.normalize(word);
|
|
387
|
+
const targetBtn = targetWords.get(key);
|
|
388
|
+
const compareBtn = compareWords.get(key);
|
|
389
|
+
if (targetBtn && compareBtn) {
|
|
390
|
+
commonFringeEfforts.push(targetBtn.effort);
|
|
391
|
+
compCommonFringeEfforts.push(compareBtn.effort);
|
|
294
392
|
commonFringeCount++;
|
|
393
|
+
}
|
|
295
394
|
});
|
|
395
|
+
const avgCommonFringeEffort = commonFringeEfforts.length > 0
|
|
396
|
+
? commonFringeEfforts.reduce((a, b) => a + b, 0) / commonFringeEfforts.length
|
|
397
|
+
: 0;
|
|
398
|
+
const avgCompCommonFringeEffort = compCommonFringeEfforts.length > 0
|
|
399
|
+
? compCommonFringeEfforts.reduce((a, b) => a + b, 0) / compCommonFringeEfforts.length
|
|
400
|
+
: 0;
|
|
401
|
+
// Common fringe component scores (matching Ruby line 702-705)
|
|
402
|
+
const commonFringeScore = avgCommonFringeEffort * 1.0;
|
|
403
|
+
const compCommonFringeScore = avgCompCommonFringeEffort * 1.0;
|
|
404
|
+
// Calculate total CARE effort tally (matching Ruby lines 707-708)
|
|
405
|
+
const PLACEHOLDER = 70;
|
|
406
|
+
const targetEffortTally = coreScore + sentenceScore + fringeScore + commonFringeScore + PLACEHOLDER;
|
|
407
|
+
const compEffortTally = compCoreScore + compSentenceScore + compFringeScore + compCommonFringeScore + PLACEHOLDER;
|
|
408
|
+
// Calculate final CARE scores (matching Ruby line 710-711)
|
|
409
|
+
// res[:target_effort_score] = [0.0, 350.0 - target_effort_tally].max
|
|
410
|
+
const careScore = Math.max(0, 350.0 - targetEffortTally);
|
|
411
|
+
const compCareScore = Math.max(0, 350.0 - compEffortTally);
|
|
296
412
|
return {
|
|
297
413
|
core: coreCount,
|
|
298
414
|
comp_core: compCoreCount,
|
|
@@ -302,6 +418,9 @@ class ComparisonAnalyzer {
|
|
|
302
418
|
comp_fringe: compFringeCount,
|
|
303
419
|
common_fringe: commonFringeCount,
|
|
304
420
|
comp_common_fringe: commonFringeCount,
|
|
421
|
+
// New composite CARE scores
|
|
422
|
+
care_score: careScore,
|
|
423
|
+
comp_care_score: compCareScore,
|
|
305
424
|
};
|
|
306
425
|
}
|
|
307
426
|
/**
|
|
@@ -263,7 +263,6 @@ class MetricsCalculator {
|
|
|
263
263
|
analyzeFrom(tree, brd, setPcts, _isRoot, options = {}) {
|
|
264
264
|
const visitedBoardIds = new Map();
|
|
265
265
|
const visitedBoardEfforts = new Map();
|
|
266
|
-
let totalButtons = 0;
|
|
267
266
|
const toVisit = [
|
|
268
267
|
{
|
|
269
268
|
board: brd,
|
|
@@ -289,12 +288,6 @@ class MetricsCalculator {
|
|
|
289
288
|
visitedBoardEfforts.set(board.id, priorEffort);
|
|
290
289
|
const rows = board.grid.length;
|
|
291
290
|
const cols = board.grid[0]?.length || 0;
|
|
292
|
-
// Count all non-empty buttons reached in this pageset
|
|
293
|
-
board.buttons.forEach((btn) => {
|
|
294
|
-
if ((btn.label || '').length > 0) {
|
|
295
|
-
totalButtons++;
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
291
|
// Calculate board-level effort
|
|
299
292
|
// Ruby uses grid size (rows * cols) for field size effort
|
|
300
293
|
const gridSize = rows * cols;
|
|
@@ -540,10 +533,13 @@ class MetricsCalculator {
|
|
|
540
533
|
}
|
|
541
534
|
levels[btn.level].push(btn);
|
|
542
535
|
});
|
|
536
|
+
// Calculate total_buttons as sum of all button counts (matching Ruby line 136)
|
|
537
|
+
// Ruby: total_buttons: buttons.map{|b| b[:count] || 1}.sum
|
|
538
|
+
const calculatedTotalButtons = buttons.reduce((sum, btn) => sum + (btn.count || 1), 0);
|
|
543
539
|
return {
|
|
544
540
|
buttons,
|
|
545
541
|
levels,
|
|
546
|
-
totalButtons,
|
|
542
|
+
totalButtons: calculatedTotalButtons,
|
|
547
543
|
visitedBoardEfforts,
|
|
548
544
|
};
|
|
549
545
|
}
|
|
@@ -77,6 +77,21 @@ export declare function distanceEffort(x: number, y: number, entryX?: number, en
|
|
|
77
77
|
* @returns Spelling effort score
|
|
78
78
|
*/
|
|
79
79
|
export declare function spellingEffort(word: string, entryEffort?: number, perLetterEffort?: number): number;
|
|
80
|
+
/**
|
|
81
|
+
* Calculate effort to access a word via prediction
|
|
82
|
+
*
|
|
83
|
+
* When prediction is available, the user:
|
|
84
|
+
* 1. Navigates to the spelling/keyboard page (entryEffort)
|
|
85
|
+
* 2. Types first 1-3 letters to trigger predictions
|
|
86
|
+
* 3. Selects from 1-3 predictions (average selections)
|
|
87
|
+
*
|
|
88
|
+
* @param entryEffort - Effort to reach the spelling/keyboard page
|
|
89
|
+
* @param perLetterEffort - Average effort per letter on the keyboard
|
|
90
|
+
* @param avgSelections - Average number of predictions to check (default 1.5)
|
|
91
|
+
* @param lettersToType - Letters to type before prediction appears (default 2)
|
|
92
|
+
* @returns Prediction effort score
|
|
93
|
+
*/
|
|
94
|
+
export declare function predictionEffort(entryEffort?: number, perLetterEffort?: number, avgSelections?: number, lettersToType?: number): number;
|
|
80
95
|
/**
|
|
81
96
|
* Calculate base board effort
|
|
82
97
|
* Combines button size and field size efforts
|
|
@@ -13,6 +13,7 @@ exports.fieldSizeEffort = fieldSizeEffort;
|
|
|
13
13
|
exports.visualScanEffort = visualScanEffort;
|
|
14
14
|
exports.distanceEffort = distanceEffort;
|
|
15
15
|
exports.spellingEffort = spellingEffort;
|
|
16
|
+
exports.predictionEffort = predictionEffort;
|
|
16
17
|
exports.baseBoardEffort = baseBoardEffort;
|
|
17
18
|
exports.applyReuseDiscount = applyReuseDiscount;
|
|
18
19
|
exports.calculateButtonEffort = calculateButtonEffort;
|
|
@@ -103,6 +104,26 @@ function distanceEffort(x, y, entryX = 1.0, entryY = 1.0) {
|
|
|
103
104
|
function spellingEffort(word, entryEffort = 10, perLetterEffort = 2.5) {
|
|
104
105
|
return entryEffort + word.length * perLetterEffort;
|
|
105
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Calculate effort to access a word via prediction
|
|
109
|
+
*
|
|
110
|
+
* When prediction is available, the user:
|
|
111
|
+
* 1. Navigates to the spelling/keyboard page (entryEffort)
|
|
112
|
+
* 2. Types first 1-3 letters to trigger predictions
|
|
113
|
+
* 3. Selects from 1-3 predictions (average selections)
|
|
114
|
+
*
|
|
115
|
+
* @param entryEffort - Effort to reach the spelling/keyboard page
|
|
116
|
+
* @param perLetterEffort - Average effort per letter on the keyboard
|
|
117
|
+
* @param avgSelections - Average number of predictions to check (default 1.5)
|
|
118
|
+
* @param lettersToType - Letters to type before prediction appears (default 2)
|
|
119
|
+
* @returns Prediction effort score
|
|
120
|
+
*/
|
|
121
|
+
function predictionEffort(entryEffort = 10, perLetterEffort = 2.5, avgSelections = 1.5, lettersToType = 2) {
|
|
122
|
+
// Cost to navigate to keyboard + type first few letters + select from predictions
|
|
123
|
+
const typingCost = lettersToType * perLetterEffort;
|
|
124
|
+
const selectionCost = avgSelections * exports.EFFORT_CONSTANTS.SCAN_SELECTION_COST;
|
|
125
|
+
return entryEffort + typingCost + selectionCost;
|
|
126
|
+
}
|
|
106
127
|
/**
|
|
107
128
|
* Calculate base board effort
|
|
108
129
|
* Combines button size and field size efforts
|
|
@@ -94,6 +94,26 @@ export interface MetricsOptions {
|
|
|
94
94
|
* Optional explicit ID of the spelling/keyboard page
|
|
95
95
|
*/
|
|
96
96
|
spellingPageId?: string;
|
|
97
|
+
/**
|
|
98
|
+
* Whether to use prediction for missing words
|
|
99
|
+
*
|
|
100
|
+
* When true (default): Words not in the board are assumed to be accessible
|
|
101
|
+
* via prediction at reduced effort (spelling_page_base + prediction_selection)
|
|
102
|
+
*
|
|
103
|
+
* When false: Words not in the board must be manually spelled at full effort
|
|
104
|
+
* (10 + word_length * 2.5 per letter)
|
|
105
|
+
*
|
|
106
|
+
* Only applies when the board has prediction capability (e.g., SwiftKey)
|
|
107
|
+
*/
|
|
108
|
+
usePrediction?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Average number of selections to find a word in prediction
|
|
111
|
+
*
|
|
112
|
+
* When prediction is enabled, this estimates how many prediction
|
|
113
|
+
* slots a user needs to check before finding their target word.
|
|
114
|
+
* Default is 1.5 (checking 1-2 predictions on average).
|
|
115
|
+
*/
|
|
116
|
+
predictionSelections?: number;
|
|
97
117
|
}
|
|
98
118
|
/**
|
|
99
119
|
* Comparison result between two board sets
|
|
@@ -143,6 +163,8 @@ export interface ComparisonResult extends MetricsResult {
|
|
|
143
163
|
comp_fringe: number;
|
|
144
164
|
common_fringe: number;
|
|
145
165
|
comp_common_fringe: number;
|
|
166
|
+
care_score: number;
|
|
167
|
+
comp_care_score: number;
|
|
146
168
|
};
|
|
147
169
|
sentences: SentenceAnalysis[];
|
|
148
170
|
fringe_words: FringeWord[];
|