Skip to content

Commit 521ba2c

Browse files
Merge branch 'master' into master
2 parents 0870287 + a3765b9 commit 521ba2c

File tree

4 files changed

+90
-36
lines changed

4 files changed

+90
-36
lines changed

frontend/src/ts/controllers/input-controller.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,11 +1182,9 @@ $("#wordsInput").on("keydown", (event) => {
11821182
}
11831183

11841184
const now = performance.now();
1185-
setTimeout(() => {
1186-
const eventCode =
1187-
event.code === "" || event.key === "Unidentified" ? "NoCode" : event.code;
1188-
TestInput.recordKeydownTime(now, eventCode);
1189-
}, 0);
1185+
const eventCode =
1186+
event.code === "" || event.key === "Unidentified" ? "NoCode" : event.code;
1187+
TestInput.recordKeydownTime(now, eventCode);
11901188
});
11911189

11921190
$("#wordsInput").on("keyup", (event) => {
@@ -1213,11 +1211,9 @@ $("#wordsInput").on("keyup", (event) => {
12131211
}
12141212

12151213
const now = performance.now();
1216-
setTimeout(() => {
1217-
const eventCode =
1218-
event.code === "" || event.key === "Unidentified" ? "NoCode" : event.code;
1219-
TestInput.recordKeyupTime(now, eventCode);
1220-
}, 0);
1214+
const eventCode =
1215+
event.code === "" || event.key === "Unidentified" ? "NoCode" : event.code;
1216+
TestInput.recordKeyupTime(now, eventCode);
12211217
});
12221218

12231219
$("#wordsInput").on("keyup", (event) => {

frontend/src/ts/test/test-input.ts

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -262,16 +262,37 @@ export function forceKeyup(now: number): void {
262262
//using mean here because for words mode, the last keypress ends the test.
263263
//if we then force keyup on that last keypress, it will record a duration of 0
264264
//skewing the average and standard deviation
265-
const avg = roundTo2(mean(keypressTimings.duration.array));
266-
const keysOrder = Object.entries(keyDownData);
267-
keysOrder.sort((a, b) => a[1].timestamp - b[1].timestamp);
268-
for (const keyOrder of keysOrder) {
269-
recordKeyupTime(now, keyOrder[0]);
265+
266+
const indexesToRemove = new Set(
267+
Object.values(keyDownData).map((data) => data.index)
268+
);
269+
270+
const keypressDurations = keypressTimings.duration.array.filter(
271+
(_, index) => !indexesToRemove.has(index)
272+
);
273+
if (keypressDurations.length === 0) {
274+
// this means the test ended while all keys were still held - probably safe to ignore
275+
// since this will result in a "too short" test anyway
276+
return;
270277
}
271-
const last = lastElementFromArray(keysOrder)?.[0] as string;
272-
const index = keyDownData[last]?.index;
273-
if (last !== undefined && index !== undefined) {
278+
279+
const avg = roundTo2(mean(keypressDurations));
280+
281+
const orderedKeys = Object.entries(keyDownData).sort(
282+
(a, b) => a[1].timestamp - b[1].timestamp
283+
);
284+
285+
for (const [key, { index }] of orderedKeys) {
274286
keypressTimings.duration.array[index] = avg;
287+
288+
if (key === "NoCode") {
289+
noCodeIndex--;
290+
}
291+
292+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
293+
delete keyDownData[key];
294+
295+
updateOverlap(now);
275296
}
276297
}
277298

@@ -350,6 +371,21 @@ function updateOverlap(now: number): void {
350371
}
351372

352373
export function resetKeypressTimings(): void {
374+
//because keydown triggers before input, we need to grab the first keypress data here and carry it over
375+
376+
//take the key with the largest index
377+
const lastKey = Object.keys(keyDownData).reduce((a, b) => {
378+
const aIndex = keyDownData[a]?.index;
379+
const bIndex = keyDownData[b]?.index;
380+
if (aIndex === undefined) return b;
381+
if (bIndex === undefined) return a;
382+
return aIndex > bIndex ? a : b;
383+
});
384+
385+
//get the data
386+
const lastKeyData = keyDownData[lastKey];
387+
388+
//reset
353389
keypressTimings = {
354390
spacing: {
355391
first: -1,
@@ -366,6 +402,26 @@ export function resetKeypressTimings(): void {
366402
};
367403
keyDownData = {};
368404
noCodeIndex = 0;
405+
406+
//carry over
407+
if (lastKeyData !== undefined) {
408+
keypressTimings = {
409+
spacing: {
410+
first: lastKeyData.timestamp,
411+
last: lastKeyData.timestamp,
412+
array: [],
413+
},
414+
duration: {
415+
array: [0],
416+
},
417+
};
418+
keyDownData[lastKey] = {
419+
timestamp: lastKeyData.timestamp,
420+
// make sure to set it to the first index
421+
index: 0,
422+
};
423+
}
424+
369425
console.debug("Keypress timings reset");
370426
}
371427

@@ -413,14 +469,4 @@ export function restart(): void {
413469
correct: 0,
414470
incorrect: 0,
415471
};
416-
keypressTimings = {
417-
spacing: {
418-
first: -1,
419-
last: -1,
420-
array: [],
421-
},
422-
duration: {
423-
array: [],
424-
},
425-
};
426472
}

frontend/src/ts/test/test-logic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -967,7 +967,7 @@ export async function finish(difficultyFailed = false): Promise<void> {
967967
}
968968

969969
// stats
970-
const stats = TestStats.calculateStats();
970+
const stats = TestStats.calculateFinalStats();
971971
if (stats.time % 1 !== 0 && Config.mode !== "time") {
972972
TestStats.setLastSecondNotRound();
973973
}

frontend/src/ts/test/test-stats.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as TestState from "./test-state";
77
import * as Numbers from "@monkeytype/util/numbers";
88
import { CompletedEvent, IncompleteTest } from "@monkeytype/schemas/results";
99
import { isFunboxActiveWithProperty } from "./funbox/list";
10+
import * as CustomText from "./custom-text";
1011

1112
type CharCount = {
1213
spaces: number;
@@ -144,14 +145,17 @@ export function calculateTestSeconds(now?: number): number {
144145
}
145146
}
146147

147-
export function calculateWpmAndRaw(withDecimalPoints?: true): {
148+
export function calculateWpmAndRaw(
149+
withDecimalPoints?: true,
150+
final = false
151+
): {
148152
wpm: number;
149153
raw: number;
150154
} {
151155
const testSeconds = calculateTestSeconds(
152156
TestState.isActive ? performance.now() : end
153157
);
154-
const chars = countChars();
158+
const chars = countChars(final);
155159
const wpm = Numbers.roundTo2(
156160
((chars.correctWordChars + chars.correctSpaces) * (60 / testSeconds)) / 5
157161
);
@@ -280,7 +284,7 @@ function getTargetWords(): string[] {
280284
return targetWords;
281285
}
282286

283-
function countChars(): CharCount {
287+
function countChars(final = false): CharCount {
284288
let correctWordChars = 0;
285289
let correctChars = 0;
286290
let incorrectChars = 0;
@@ -343,7 +347,13 @@ function countChars(): CharCount {
343347
}
344348
correctChars += toAdd.correct;
345349
incorrectChars += toAdd.incorrect;
346-
if (i === inputWords.length - 1) {
350+
351+
const isTimedTest =
352+
Config.mode === "time" ||
353+
(Config.mode === "custom" && CustomText.getLimit().mode === "time");
354+
const shouldCountPartialLastWord = !final || (final && isTimedTest);
355+
356+
if (i === inputWords.length - 1 && shouldCountPartialLastWord) {
347357
//last word - check if it was all correct - add to correct word chars
348358
if (toAdd.incorrect === 0) correctWordChars += toAdd.correct;
349359
} else {
@@ -370,7 +380,7 @@ function countChars(): CharCount {
370380
};
371381
}
372382

373-
export function calculateStats(): Stats {
383+
export function calculateFinalStats(): Stats {
374384
console.debug("Calculating result stats");
375385
let testSeconds = calculateTestSeconds();
376386
console.debug(
@@ -398,8 +408,10 @@ export function calculateStats(): Stats {
398408
testSeconds
399409
);
400410
}
401-
const chars = countChars();
402-
const { wpm, raw } = calculateWpmAndRaw(true);
411+
412+
//todo: this counts chars twice - once here and once in calculateWpmAndRaw
413+
const chars = countChars(true);
414+
const { wpm, raw } = calculateWpmAndRaw(true, true);
403415
const acc = Numbers.roundTo2(calculateAccuracy());
404416
const ret = {
405417
wpm: isNaN(wpm) ? 0 : wpm,

0 commit comments

Comments
 (0)