@vue-skuilder/common 0.1.24 → 0.1.25
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/course-data.d.ts +38 -4
- package/dist/course-data.d.ts.map +1 -1
- package/dist/course-data.js +7 -0
- package/dist/course-data.js.map +1 -1
- package/dist/course-data.mjs +6 -0
- package/dist/elo.d.ts +25 -0
- package/dist/elo.d.ts.map +1 -1
- package/dist/elo.js +59 -0
- package/dist/elo.js.map +1 -1
- package/dist/elo.mjs +58 -0
- package/package.json +2 -2
- package/src/course-data.ts +43 -5
- package/src/elo.ts +76 -0
package/dist/course-data.d.ts
CHANGED
|
@@ -10,8 +10,42 @@ export interface Evaluation {
|
|
|
10
10
|
isCorrect: boolean;
|
|
11
11
|
performance: Performance;
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Performance can be a simple number (0-1) for overall score,
|
|
15
|
+
* or a structured object with per-tag granularity.
|
|
16
|
+
*/
|
|
17
|
+
export type Performance = number | TaggedPerformance;
|
|
18
|
+
/**
|
|
19
|
+
* Structured performance with per-tag scoring.
|
|
20
|
+
*
|
|
21
|
+
* Questions that exercise multiple skills (e.g., spelling with multiple GPCs)
|
|
22
|
+
* can provide individual scores per tag for granular ELO updates.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // Spelling "cat" as "kat" - got 'a' and 't' right, but 'c' wrong
|
|
26
|
+
* {
|
|
27
|
+
* _global: 0.67, // 2/3 correct, used for SRS and global ELO
|
|
28
|
+
* 'GPC-c-K': 0, // incorrect
|
|
29
|
+
* 'GPC-a-AE': 1, // correct
|
|
30
|
+
* 'GPC-t-T': 1, // correct
|
|
31
|
+
* }
|
|
32
|
+
*/
|
|
33
|
+
export interface TaggedPerformance {
|
|
34
|
+
/**
|
|
35
|
+
* Overall score for SRS scheduling and global ELO updates.
|
|
36
|
+
* Required when using structured performance.
|
|
37
|
+
* Range: [0, 1]
|
|
38
|
+
*/
|
|
39
|
+
_global: number;
|
|
40
|
+
/**
|
|
41
|
+
* Per-tag scores. Keys are tag IDs (e.g., 'GPC-c-K').
|
|
42
|
+
* Tags not present on the card will be created dynamically.
|
|
43
|
+
* Range: [0, 1] for each value.
|
|
44
|
+
*/
|
|
45
|
+
[tag: string]: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Type guard to check if performance is structured (TaggedPerformance).
|
|
49
|
+
*/
|
|
50
|
+
export declare function isTaggedPerformance(p: Performance): p is TaggedPerformance;
|
|
17
51
|
//# sourceMappingURL=course-data.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"course-data.d.ts","sourceRoot":"","sources":["../src/course-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAW,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAKtD,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,SAAS,EAGhB,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,CAAC,EAAE;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,cAAc,CAAA;CAAE,GACrD,eAAe,CAsFjB;AAED;;GAEG;AAEH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,
|
|
1
|
+
{"version":3,"file":"course-data.d.ts","sourceRoot":"","sources":["../src/course-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAW,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAKtD,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,SAAS,EAGhB,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,CAAC,EAAE;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,cAAc,CAAA;CAAE,GACrD,eAAe,CAsFjB;AAED;;GAEG;AAEH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,iBAAiB,CAAC;AAErD;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;OAIG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,WAAW,GAAG,CAAC,IAAI,iBAAiB,CAE1E"}
|
package/dist/course-data.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.prepareNote55 = prepareNote55;
|
|
4
|
+
exports.isTaggedPerformance = isTaggedPerformance;
|
|
4
5
|
const db_js_1 = require("./db.js");
|
|
5
6
|
const namespacer_js_1 = require("./namespacer.js");
|
|
6
7
|
const FieldType_js_1 = require("./enums/FieldType.js");
|
|
@@ -84,4 +85,10 @@ data, author, _tags, uploads) {
|
|
|
84
85
|
});
|
|
85
86
|
return payload;
|
|
86
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Type guard to check if performance is structured (TaggedPerformance).
|
|
90
|
+
*/
|
|
91
|
+
function isTaggedPerformance(p) {
|
|
92
|
+
return typeof p === 'object' && p !== null && '_global' in p;
|
|
93
|
+
}
|
|
87
94
|
//# sourceMappingURL=course-data.js.map
|
package/dist/course-data.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"course-data.js","sourceRoot":"","sources":["../src/course-data.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"course-data.js","sourceRoot":"","sources":["../src/course-data.ts"],"names":[],"mappings":"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"}
|
package/dist/course-data.mjs
CHANGED
|
@@ -81,4 +81,10 @@ data, author, _tags, uploads) {
|
|
|
81
81
|
});
|
|
82
82
|
return payload;
|
|
83
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Type guard to check if performance is structured (TaggedPerformance).
|
|
86
|
+
*/
|
|
87
|
+
export function isTaggedPerformance(p) {
|
|
88
|
+
return typeof p === 'object' && p !== null && '_global' in p;
|
|
89
|
+
}
|
|
84
90
|
//# sourceMappingURL=course-data.js.map
|
package/dist/elo.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TaggedPerformance } from './course-data.js';
|
|
1
2
|
export declare class EloRanker {
|
|
2
3
|
k: number;
|
|
3
4
|
constructor(k?: number);
|
|
@@ -40,5 +41,29 @@ export declare function adjustCourseScores(aElo: Eloish, bElo: Eloish, userScore
|
|
|
40
41
|
userElo: CourseElo;
|
|
41
42
|
cardElo: CourseElo;
|
|
42
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* Adjusts ELO scores with per-tag granularity.
|
|
46
|
+
*
|
|
47
|
+
* Unlike adjustCourseScores which applies the same score to all tags,
|
|
48
|
+
* this function allows different scores per tag for granular skill tracking.
|
|
49
|
+
*
|
|
50
|
+
* @param aElo - User's current ELO (will be converted to CourseElo)
|
|
51
|
+
* @param bElo - Card's current ELO (will be converted to CourseElo)
|
|
52
|
+
* @param taggedPerformance - Object with _global score and per-tag scores
|
|
53
|
+
* @returns Updated user and card ELOs
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* // Spelling "cat" as "kat" - got 'a' and 't' right, but 'c' wrong
|
|
57
|
+
* adjustCourseScoresPerTag(userElo, cardElo, {
|
|
58
|
+
* _global: 0.67,
|
|
59
|
+
* 'GPC-c-K': 0,
|
|
60
|
+
* 'GPC-a-AE': 1,
|
|
61
|
+
* 'GPC-t-T': 1,
|
|
62
|
+
* });
|
|
63
|
+
*/
|
|
64
|
+
export declare function adjustCourseScoresPerTag(aElo: Eloish, bElo: Eloish, taggedPerformance: TaggedPerformance): {
|
|
65
|
+
userElo: CourseElo;
|
|
66
|
+
cardElo: CourseElo;
|
|
67
|
+
};
|
|
43
68
|
export {};
|
|
44
69
|
//# sourceMappingURL=elo.d.ts.map
|
package/dist/elo.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elo.d.ts","sourceRoot":"","sources":["../src/elo.ts"],"names":[],"mappings":"AAAA,qBAAa,SAAS;IACD,CAAC,EAAE,MAAM;gBAAT,CAAC,GAAE,MAAW;IAEjC,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAG3B,UAAU,IAAI,MAAM;IAIpB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;IAGzC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;CAGxE;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE;QACJ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;KAC1B,CAAC;IACF,IAAI,EAAE;QACJ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF,KAAK,OAAO,GAAG;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3C,wBAAgB,cAAc,IAAI,SAAS,CAS1C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAS/C;AACD,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CASpD;AACD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CA+B9D;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,SAAS,CAMtD;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;IACR,UAAU,EAAE,OAAO,CAAC;CACrB,GACA;IACD,OAAO,EAAE,SAAS,CAAC;IACnB,OAAO,EAAE,SAAS,CAAC;CACpB,CA+BA"}
|
|
1
|
+
{"version":3,"file":"elo.d.ts","sourceRoot":"","sources":["../src/elo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,qBAAa,SAAS;IACD,CAAC,EAAE,MAAM;gBAAT,CAAC,GAAE,MAAW;IAEjC,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAG3B,UAAU,IAAI,MAAM;IAIpB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;IAGzC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;CAGxE;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE;QACJ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;KAC1B,CAAC;IACF,IAAI,EAAE;QACJ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF,KAAK,OAAO,GAAG;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3C,wBAAgB,cAAc,IAAI,SAAS,CAS1C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAS/C;AACD,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CASpD;AACD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CA+B9D;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,SAAS,CAMtD;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;IACR,UAAU,EAAE,OAAO,CAAC;CACrB,GACA;IACD,OAAO,EAAE,SAAS,CAAC;IACnB,OAAO,EAAE,SAAS,CAAC;CACpB,CA+BA;AAqCD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,iBAAiB,EAAE,iBAAiB,GACnC;IACD,OAAO,EAAE,SAAS,CAAC;IACnB,OAAO,EAAE,SAAS,CAAC;CACpB,CA6CA"}
|
package/dist/elo.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.toElo = toElo;
|
|
|
7
7
|
exports.toCourseElo = toCourseElo;
|
|
8
8
|
exports.isCourseElo = isCourseElo;
|
|
9
9
|
exports.adjustCourseScores = adjustCourseScores;
|
|
10
|
+
exports.adjustCourseScoresPerTag = adjustCourseScoresPerTag;
|
|
10
11
|
class EloRanker {
|
|
11
12
|
k;
|
|
12
13
|
constructor(k = 32) {
|
|
@@ -159,4 +160,62 @@ function adjustScores(userElo, cardElo, userScore) {
|
|
|
159
160
|
},
|
|
160
161
|
};
|
|
161
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Adjusts ELO scores with per-tag granularity.
|
|
165
|
+
*
|
|
166
|
+
* Unlike adjustCourseScores which applies the same score to all tags,
|
|
167
|
+
* this function allows different scores per tag for granular skill tracking.
|
|
168
|
+
*
|
|
169
|
+
* @param aElo - User's current ELO (will be converted to CourseElo)
|
|
170
|
+
* @param bElo - Card's current ELO (will be converted to CourseElo)
|
|
171
|
+
* @param taggedPerformance - Object with _global score and per-tag scores
|
|
172
|
+
* @returns Updated user and card ELOs
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* // Spelling "cat" as "kat" - got 'a' and 't' right, but 'c' wrong
|
|
176
|
+
* adjustCourseScoresPerTag(userElo, cardElo, {
|
|
177
|
+
* _global: 0.67,
|
|
178
|
+
* 'GPC-c-K': 0,
|
|
179
|
+
* 'GPC-a-AE': 1,
|
|
180
|
+
* 'GPC-t-T': 1,
|
|
181
|
+
* });
|
|
182
|
+
*/
|
|
183
|
+
function adjustCourseScoresPerTag(aElo, bElo, taggedPerformance) {
|
|
184
|
+
const globalScore = taggedPerformance._global;
|
|
185
|
+
if (globalScore < 0 || globalScore > 1) {
|
|
186
|
+
throw new Error(`ELO _global score must be between 0 and 1 - received ${globalScore}`);
|
|
187
|
+
}
|
|
188
|
+
const userElo = toCourseElo(aElo);
|
|
189
|
+
const cardElo = toCourseElo(bElo);
|
|
190
|
+
// Process each tag in the performance object
|
|
191
|
+
for (const [key, tagScore] of Object.entries(taggedPerformance)) {
|
|
192
|
+
if (key === '_global')
|
|
193
|
+
continue;
|
|
194
|
+
if (typeof tagScore !== 'number' || tagScore < 0 || tagScore > 1) {
|
|
195
|
+
throw new Error(`ELO tag score for '${key}' must be between 0 and 1 - received ${tagScore}`);
|
|
196
|
+
}
|
|
197
|
+
// Initialize tag ELO on user if missing (use global score as baseline)
|
|
198
|
+
const userTagElo = userElo.tags[key] ?? {
|
|
199
|
+
count: 0,
|
|
200
|
+
score: userElo.global.score,
|
|
201
|
+
};
|
|
202
|
+
// Initialize tag ELO on card if missing (use global score as baseline)
|
|
203
|
+
const cardTagElo = cardElo.tags[key] ?? {
|
|
204
|
+
count: 0,
|
|
205
|
+
score: cardElo.global.score,
|
|
206
|
+
};
|
|
207
|
+
// Apply per-tag score
|
|
208
|
+
const adjusted = adjustScores(userTagElo, cardTagElo, tagScore);
|
|
209
|
+
userElo.tags[key] = adjusted.userElo;
|
|
210
|
+
cardElo.tags[key] = adjusted.cardElo;
|
|
211
|
+
}
|
|
212
|
+
// Apply global score to global ELO
|
|
213
|
+
const adjustedGlobal = adjustScores(userElo.global, cardElo.global, globalScore);
|
|
214
|
+
userElo.global = adjustedGlobal.userElo;
|
|
215
|
+
cardElo.global = adjustedGlobal.cardElo;
|
|
216
|
+
return {
|
|
217
|
+
userElo,
|
|
218
|
+
cardElo,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
162
221
|
//# sourceMappingURL=elo.js.map
|
package/dist/elo.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elo.js","sourceRoot":"","sources":["../src/elo.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"elo.js","sourceRoot":"","sources":["../src/elo.ts"],"names":[],"mappings":"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"}
|
package/dist/elo.mjs
CHANGED
|
@@ -149,4 +149,62 @@ function adjustScores(userElo, cardElo, userScore) {
|
|
|
149
149
|
},
|
|
150
150
|
};
|
|
151
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Adjusts ELO scores with per-tag granularity.
|
|
154
|
+
*
|
|
155
|
+
* Unlike adjustCourseScores which applies the same score to all tags,
|
|
156
|
+
* this function allows different scores per tag for granular skill tracking.
|
|
157
|
+
*
|
|
158
|
+
* @param aElo - User's current ELO (will be converted to CourseElo)
|
|
159
|
+
* @param bElo - Card's current ELO (will be converted to CourseElo)
|
|
160
|
+
* @param taggedPerformance - Object with _global score and per-tag scores
|
|
161
|
+
* @returns Updated user and card ELOs
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* // Spelling "cat" as "kat" - got 'a' and 't' right, but 'c' wrong
|
|
165
|
+
* adjustCourseScoresPerTag(userElo, cardElo, {
|
|
166
|
+
* _global: 0.67,
|
|
167
|
+
* 'GPC-c-K': 0,
|
|
168
|
+
* 'GPC-a-AE': 1,
|
|
169
|
+
* 'GPC-t-T': 1,
|
|
170
|
+
* });
|
|
171
|
+
*/
|
|
172
|
+
export function adjustCourseScoresPerTag(aElo, bElo, taggedPerformance) {
|
|
173
|
+
const globalScore = taggedPerformance._global;
|
|
174
|
+
if (globalScore < 0 || globalScore > 1) {
|
|
175
|
+
throw new Error(`ELO _global score must be between 0 and 1 - received ${globalScore}`);
|
|
176
|
+
}
|
|
177
|
+
const userElo = toCourseElo(aElo);
|
|
178
|
+
const cardElo = toCourseElo(bElo);
|
|
179
|
+
// Process each tag in the performance object
|
|
180
|
+
for (const [key, tagScore] of Object.entries(taggedPerformance)) {
|
|
181
|
+
if (key === '_global')
|
|
182
|
+
continue;
|
|
183
|
+
if (typeof tagScore !== 'number' || tagScore < 0 || tagScore > 1) {
|
|
184
|
+
throw new Error(`ELO tag score for '${key}' must be between 0 and 1 - received ${tagScore}`);
|
|
185
|
+
}
|
|
186
|
+
// Initialize tag ELO on user if missing (use global score as baseline)
|
|
187
|
+
const userTagElo = userElo.tags[key] ?? {
|
|
188
|
+
count: 0,
|
|
189
|
+
score: userElo.global.score,
|
|
190
|
+
};
|
|
191
|
+
// Initialize tag ELO on card if missing (use global score as baseline)
|
|
192
|
+
const cardTagElo = cardElo.tags[key] ?? {
|
|
193
|
+
count: 0,
|
|
194
|
+
score: cardElo.global.score,
|
|
195
|
+
};
|
|
196
|
+
// Apply per-tag score
|
|
197
|
+
const adjusted = adjustScores(userTagElo, cardTagElo, tagScore);
|
|
198
|
+
userElo.tags[key] = adjusted.userElo;
|
|
199
|
+
cardElo.tags[key] = adjusted.cardElo;
|
|
200
|
+
}
|
|
201
|
+
// Apply global score to global ELO
|
|
202
|
+
const adjustedGlobal = adjustScores(userElo.global, cardElo.global, globalScore);
|
|
203
|
+
userElo.global = adjustedGlobal.userElo;
|
|
204
|
+
cardElo.global = adjustedGlobal.cardElo;
|
|
205
|
+
return {
|
|
206
|
+
userElo,
|
|
207
|
+
cardElo,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
152
210
|
//# sourceMappingURL=elo.js.map
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.25",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"module": "dist/index.mjs",
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"zod": "^3.23.8",
|
|
37
37
|
"zod-to-json-schema": "^3.23.5"
|
|
38
38
|
},
|
|
39
|
-
"stableVersion": "0.1.
|
|
39
|
+
"stableVersion": "0.1.25"
|
|
40
40
|
}
|
package/src/course-data.ts
CHANGED
|
@@ -112,8 +112,46 @@ export interface Evaluation {
|
|
|
112
112
|
performance: Performance;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Performance can be a simple number (0-1) for overall score,
|
|
117
|
+
* or a structured object with per-tag granularity.
|
|
118
|
+
*/
|
|
119
|
+
export type Performance = number | TaggedPerformance;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Structured performance with per-tag scoring.
|
|
123
|
+
*
|
|
124
|
+
* Questions that exercise multiple skills (e.g., spelling with multiple GPCs)
|
|
125
|
+
* can provide individual scores per tag for granular ELO updates.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* // Spelling "cat" as "kat" - got 'a' and 't' right, but 'c' wrong
|
|
129
|
+
* {
|
|
130
|
+
* _global: 0.67, // 2/3 correct, used for SRS and global ELO
|
|
131
|
+
* 'GPC-c-K': 0, // incorrect
|
|
132
|
+
* 'GPC-a-AE': 1, // correct
|
|
133
|
+
* 'GPC-t-T': 1, // correct
|
|
134
|
+
* }
|
|
135
|
+
*/
|
|
136
|
+
export interface TaggedPerformance {
|
|
137
|
+
/**
|
|
138
|
+
* Overall score for SRS scheduling and global ELO updates.
|
|
139
|
+
* Required when using structured performance.
|
|
140
|
+
* Range: [0, 1]
|
|
141
|
+
*/
|
|
142
|
+
_global: number;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Per-tag scores. Keys are tag IDs (e.g., 'GPC-c-K').
|
|
146
|
+
* Tags not present on the card will be created dynamically.
|
|
147
|
+
* Range: [0, 1] for each value.
|
|
148
|
+
*/
|
|
149
|
+
[tag: string]: number;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Type guard to check if performance is structured (TaggedPerformance).
|
|
154
|
+
*/
|
|
155
|
+
export function isTaggedPerformance(p: Performance): p is TaggedPerformance {
|
|
156
|
+
return typeof p === 'object' && p !== null && '_global' in p;
|
|
157
|
+
}
|
package/src/elo.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { TaggedPerformance } from './course-data.js';
|
|
2
|
+
|
|
1
3
|
export class EloRanker {
|
|
2
4
|
constructor(public k: number = 32) {}
|
|
3
5
|
|
|
@@ -191,3 +193,77 @@ function adjustScores(
|
|
|
191
193
|
},
|
|
192
194
|
};
|
|
193
195
|
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Adjusts ELO scores with per-tag granularity.
|
|
199
|
+
*
|
|
200
|
+
* Unlike adjustCourseScores which applies the same score to all tags,
|
|
201
|
+
* this function allows different scores per tag for granular skill tracking.
|
|
202
|
+
*
|
|
203
|
+
* @param aElo - User's current ELO (will be converted to CourseElo)
|
|
204
|
+
* @param bElo - Card's current ELO (will be converted to CourseElo)
|
|
205
|
+
* @param taggedPerformance - Object with _global score and per-tag scores
|
|
206
|
+
* @returns Updated user and card ELOs
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* // Spelling "cat" as "kat" - got 'a' and 't' right, but 'c' wrong
|
|
210
|
+
* adjustCourseScoresPerTag(userElo, cardElo, {
|
|
211
|
+
* _global: 0.67,
|
|
212
|
+
* 'GPC-c-K': 0,
|
|
213
|
+
* 'GPC-a-AE': 1,
|
|
214
|
+
* 'GPC-t-T': 1,
|
|
215
|
+
* });
|
|
216
|
+
*/
|
|
217
|
+
export function adjustCourseScoresPerTag(
|
|
218
|
+
aElo: Eloish,
|
|
219
|
+
bElo: Eloish,
|
|
220
|
+
taggedPerformance: TaggedPerformance
|
|
221
|
+
): {
|
|
222
|
+
userElo: CourseElo;
|
|
223
|
+
cardElo: CourseElo;
|
|
224
|
+
} {
|
|
225
|
+
const globalScore = taggedPerformance._global;
|
|
226
|
+
|
|
227
|
+
if (globalScore < 0 || globalScore > 1) {
|
|
228
|
+
throw new Error(`ELO _global score must be between 0 and 1 - received ${globalScore}`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const userElo: CourseElo = toCourseElo(aElo);
|
|
232
|
+
const cardElo: CourseElo = toCourseElo(bElo);
|
|
233
|
+
|
|
234
|
+
// Process each tag in the performance object
|
|
235
|
+
for (const [key, tagScore] of Object.entries(taggedPerformance)) {
|
|
236
|
+
if (key === '_global') continue;
|
|
237
|
+
|
|
238
|
+
if (typeof tagScore !== 'number' || tagScore < 0 || tagScore > 1) {
|
|
239
|
+
throw new Error(`ELO tag score for '${key}' must be between 0 and 1 - received ${tagScore}`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Initialize tag ELO on user if missing (use global score as baseline)
|
|
243
|
+
const userTagElo: EloRank = userElo.tags[key] ?? {
|
|
244
|
+
count: 0,
|
|
245
|
+
score: userElo.global.score,
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// Initialize tag ELO on card if missing (use global score as baseline)
|
|
249
|
+
const cardTagElo: EloRank = cardElo.tags[key] ?? {
|
|
250
|
+
count: 0,
|
|
251
|
+
score: cardElo.global.score,
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// Apply per-tag score
|
|
255
|
+
const adjusted = adjustScores(userTagElo, cardTagElo, tagScore);
|
|
256
|
+
userElo.tags[key] = adjusted.userElo;
|
|
257
|
+
cardElo.tags[key] = adjusted.cardElo;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Apply global score to global ELO
|
|
261
|
+
const adjustedGlobal = adjustScores(userElo.global, cardElo.global, globalScore);
|
|
262
|
+
userElo.global = adjustedGlobal.userElo;
|
|
263
|
+
cardElo.global = adjustedGlobal.cardElo;
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
userElo,
|
|
267
|
+
cardElo,
|
|
268
|
+
};
|
|
269
|
+
}
|