qb-answer-checker 1.0.0 → 1.0.2

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/README.md CHANGED
@@ -1,4 +1,11 @@
1
- This document specifies the kind of (quizbowl) answerlines that the program is designed to parse.
1
+ # QB Answer Checker
2
+
3
+ A package to automatically check/judge answers against quizbowl answerlines.
4
+ Mostly used in conjunction with [qbreader](https://www.qbreader.org/) to automatically check answers.
5
+
6
+ ## Answerline Specification
7
+
8
+ This section specifies the kind of (quizbowl) answerlines that the program is designed to parse.
2
9
 
3
10
  **Answerlines** should be formatted as follows:
4
11
 
@@ -30,7 +37,7 @@ Each **directive** should be one of:
30
37
 
31
38
  and "on" and "by asking/with" are optional and indicate that there should be a directed prompt.
32
39
 
33
- ## Special Directives
40
+ ### Special Directives
34
41
 
35
42
  **Special directives** should be one of the following and affect the main answerline only:
36
43
 
@@ -41,7 +48,7 @@ and "on" and "by asking/with" are optional and indicate that there should be a d
41
48
 
42
49
  **Note:** special directives should be the first phrase in the sub-answerline, but this program will recognize them anywhere in the sub-answerline.
43
50
 
44
- ## Additional Info
51
+ ### Additional Info
45
52
 
46
53
  For more information about how answerlines should be formatted, see <https://minkowski.space/quizbowl/manuals/style/answerlines.html>.
47
54
  Note that the linked guide is more useful for explaining how answerlines should be formatted from a sylistic/quizbowl sense, while this specification only describes how they should be formatted in a way that computers can understand.
@@ -0,0 +1,4 @@
1
+ declare module 'qb-answer-checker' {
2
+ function checkAnswer(answer: string, givenAnswer: string): boolean;
3
+ export = checkAnswer;
4
+ }
@@ -158,7 +158,12 @@ function parseAnswerline(answerline) {
158
158
  }
159
159
 
160
160
 
161
- const generateTokens = (string) => {
161
+ /**
162
+ * Generates standardized tokens from a string.
163
+ * @param {string} string
164
+ * @returns {string[]} the tokens generated from the string
165
+ */
166
+ function generateTokens(string) {
162
167
  const tokens = string.split(' ')
163
168
  .filter(token => token.length > 0)
164
169
  .map(string => standardizeTokens(string));
@@ -200,7 +205,7 @@ const generateTokens = (string) => {
200
205
  * @param {boolean} acceptSubstring
201
206
  * @param {number} strictness
202
207
  * @param {boolean} useStemmer
203
- * @returns
208
+ * @returns {boolean}
204
209
  */
205
210
  function tokenListsMatch(given, reference, acceptSubstring, strictness, useStemmer) {
206
211
  let j = 0;
@@ -216,7 +221,7 @@ function tokenListsMatch(given, reference, acceptSubstring, strictness, useStemm
216
221
  errors = distance(given[i], reference[j]);
217
222
  }
218
223
 
219
- if (strictness * errors <= reference[j].length) {
224
+ if (strictness > 0 && strictness * errors <= reference[j].length) {
220
225
  matches = true;
221
226
  }
222
227
 
@@ -224,6 +229,10 @@ function tokenListsMatch(given, reference, acceptSubstring, strictness, useStemm
224
229
  matches = true;
225
230
  }
226
231
 
232
+ if (errors === 0) {
233
+ matches = true;
234
+ }
235
+
227
236
  j++;
228
237
  }
229
238
 
@@ -237,20 +246,23 @@ function tokenListsMatch(given, reference, acceptSubstring, strictness, useStemm
237
246
 
238
247
  /**
239
248
  * Returns true if and only if every token in `string` is present in `reference`.
240
- * @param {String} string
241
- * @param {String} reference
242
- * @param {Number} strictness - the number of characters per error allowed for two tokens to match.
243
- * @param {Boolean} acceptSubstring - whether or not to accept substrings.
244
- * @param {Boolean} useStemmer - whether or not to use a stemmer.
245
- * @param {Boolean} respectOrder - whether or not to respect the order of the tokens (i.e. "a b" is not the same as "b a").
246
- * @returns {Boolean}
249
+ * @param {object} options
250
+ * @param {string} options.string
251
+ * @param {string} options.reference
252
+ * @param {number} [options.strictness=7] - the number of characters per error allowed for two tokens to match. If set to a negative number or 0, then no errors are allowed.
253
+ * @param {boolean} [options.acceptSubstring=false] - whether or not to accept substrings.
254
+ * @param {boolean} [options.useStemmer=true] - whether or not to use a stemmer.
255
+ * @param {boolean} [options.respectOrder=false] - whether or not to respect the order of the tokens (i.e. "a b" is not the same as "b a").
256
+ * @returns {boolean}
247
257
  */
248
- function stringMatchesReference({ string, reference, strictness = 5, acceptSubstring = false, useStemmer = true, respectOrder = false }) {
249
- if (string === null || string === undefined || reference === null || reference === undefined)
258
+ function stringMatchesReference({ string, reference, strictness = 7, acceptSubstring = false, useStemmer = true, respectOrder = false }) {
259
+ if (string === null || string === undefined || reference === null || reference === undefined) {
250
260
  return false;
261
+ }
251
262
 
252
- if (string.length === 0)
263
+ if (string.length === 0) {
253
264
  return false;
265
+ }
254
266
 
255
267
  string = utils.removePunctuation(string).trim();
256
268
  reference = utils.removePunctuation(reference).trim();
@@ -319,10 +331,10 @@ function checkAnswer(answerline, givenAnswer) {
319
331
  for (const answer of parsedAnswerline.reject) {
320
332
  const useStemmer = (stemmer(answer) !== stemmer(parsedAnswerline.accept[0]));
321
333
 
322
- if (!stringMatchesReference({ string: answer, reference: givenAnswer, strictness: 11, useStemmer }))
334
+ if (!stringMatchesReference({ string: answer, reference: givenAnswer, strictness: -1, useStemmer }))
323
335
  continue;
324
336
 
325
- if (!stringMatchesReference({ string: givenAnswer, reference: answer, strictness: 11, useStemmer }))
337
+ if (!stringMatchesReference({ string: givenAnswer, reference: answer, strictness: -1, useStemmer }))
326
338
  continue;
327
339
 
328
340
  return { directive: 'reject', directedPrompt: null };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qb-answer-checker",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "description": "A package to automatically check/judge answers against quizbowl answerlines.",
6
6
  "main": "lib/check-answer.js",
package/test/script.js CHANGED
@@ -1,4 +1,4 @@
1
- import checkAnswer from 'qb-answer-checker';
1
+ import checkAnswer from '../lib/check-answer.js';
2
2
  import * as bcolors from './bcolors.js';
3
3
 
4
4
  import { assert } from 'chai';
package/test/tests.json CHANGED
@@ -1,5 +1,19 @@
1
1
  {
2
2
  "formatted": [
3
+ {
4
+ "answerline": "<b><u>realignments</u></b> [reject \"dealignments\"]",
5
+ "tests": [
6
+ { "directive": "accept", "given": "realignments" },
7
+ { "directive": "reject", "given": "dealignments" }
8
+ ]
9
+ },
10
+ {
11
+ "answerline": "<b><u>dihydroxylation</u></b> [prompt on <b><u>hydroxylation</u></b>]",
12
+ "tests": [
13
+ { "directive": "accept", "given": "dihydroxylation" },
14
+ { "directive": "prompt", "given": "hydroxylation" }
15
+ ]
16
+ },
3
17
  {
4
18
  "answerline": "<b><u>Major Major Major</u></b>",
5
19
  "tests": [