Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | 76x 50x 38x 103x 38x 38x 38x 38x 50x 50x 50x 50x 50x 50x 44x 2x 2x 2x 186x 50x 50x 1x 1x 1x 1x 13x 13x 13x 13x 24x 24x 38x 4x | import * as CubeFacelets from '@wstein/regrip-core/domain/CubeFacelets';
import type { SmartCubeSessionEvent } from '@wstein/regrip-core/session/smartCubeSession';
export type SolveAnalysisSnapshot = {
status: 'empty' | 'running' | 'complete';
durationMs: number;
tps: number | null;
moveCount: number;
qtm: number;
regrips: number;
triggers: number;
longestPauseMs: number;
};
const emptySnapshot = (): SolveAnalysisSnapshot => ({
status: 'empty',
durationMs: 0,
tps: null,
moveCount: 0,
qtm: 0,
regrips: 0,
triggers: 0,
longestPauseMs: 0,
});
function quarterTurns(move: string): number {
return /2'?$/.test(move.trim()) ? 2 : 1;
}
/** Summarize the current connection/replay run from its first physical turn. */
export function createSolveAnalysis(onChange?: (snapshot: SolveAnalysisSnapshot) => void) {
let value = emptySnapshot();
let startedAt: number | undefined;
let lastMoveAt: number | undefined;
const publish = (): void => onChange?.({ ...value });
function reset(): void {
value = emptySnapshot();
startedAt = undefined;
lastMoveAt = undefined;
publish();
}
function onMove(event: Extract<SmartCubeSessionEvent, { type: 'MOVE' }>): void {
if (value.status === 'complete') reset();
startedAt ??= event.timestamp;
const pause = lastMoveAt === undefined ? 0 : Math.max(0, event.timestamp - lastMoveAt);
lastMoveAt = event.timestamp;
value = {
...value,
status: 'running',
durationMs: Math.max(0, event.timestamp - startedAt),
moveCount: value.moveCount + 1,
qtm: value.qtm + quarterTurns(event.move),
longestPauseMs: Math.max(value.longestPauseMs, pause),
};
publish();
}
function complete(): void {
if (value.status !== 'running' || startedAt === undefined || lastMoveAt === undefined) return;
const durationMs = Math.max(0, lastMoveAt - startedAt);
value = {
...value,
status: 'complete',
durationMs,
tps: durationMs > 0 ? value.moveCount / (durationMs / 1000) : null,
};
publish();
}
function onEvent(event: SmartCubeSessionEvent): void {
switch (event.type) {
case 'MOVE':
onMove(event);
break;
case 'REGRIP':
if (value.status === 'running') {
value = { ...value, regrips: value.regrips + 1 };
publish();
}
break;
case 'CUSTOM_TRIGGER':
case 'SHAKE':
if (value.status === 'running') {
value = { ...value, triggers: value.triggers + 1 };
publish();
}
break;
case 'FACELETS':
if (CubeFacelets.isSolvedFacelets(event.facelets)) complete();
break;
}
}
return {
onEvent,
complete,
reset,
snapshot: (): SolveAnalysisSnapshot => ({ ...value }),
};
}
export type SolveAnalysis = ReturnType<typeof createSolveAnalysis>;
|