Skip to content

Commit

Permalink
improved answer checking and prompting
Browse files Browse the repository at this point in the history
  • Loading branch information
geoffrey-wu committed Oct 28, 2022
1 parent 1f3e018 commit f5af784
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 12 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "qbreader",
"version": "2.6.1",
"version": "2.6.2",
"scripts": {
"start": "node server/server.js",
"sass": "sass scss/light.scss client/bootstrap/light.css && sass scss/dark.scss client/bootstrap/dark.css"
Expand Down
56 changes: 48 additions & 8 deletions server/quizbowl.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,14 @@ function parseAnswerline(answerline) {
}


function stringMatchesReference(string, reference) {
/**
*
* @param {String} string
* @param {String} reference
* @param {Number} strictness - the number of characters per error allowed for two tokens to match.
* @returns {Boolean}
*/
function stringMatchesReference(string, reference, strictness=4) {
if (string === null || string === undefined || reference === null || reference === undefined) {
return false;
}
Expand All @@ -127,9 +134,19 @@ function stringMatchesReference(string, reference) {
return string.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}

const stemmer = (string) => {
if (string.charAt(string.length - 1) === 's') {
return string.substring(0, string.length - 1);
} else {
return string;
}
}

string = removePunctuation(string);
string = replaceSpecialCharacters(string);
string = string.toLowerCase().trim();
string = string.replace(/<\/?[biu]>/g, '');
string = string.replace(/<\/?em>/g, '');
string = string.replace('-', ' ');

reference = removePunctuation(reference);
Expand All @@ -143,12 +160,22 @@ function stringMatchesReference(string, reference) {

let stringTokens = string
.split(' ')
.filter(token => !METAWORDS.includes(token) && token.length > 0)
.map(token => isNaN(token) ? token : toWords(parseInt(token)));
.filter(token => !METAWORDS.includes(token) && token.length > 0);

for (let i = stringTokens.length - 1; i >= 0; i--) {
if (!isNaN(stringTokens[i])) {
stringTokens.push(toWords(parseInt(stringTokens[i])));
}
}

let referenceTokens = reference
.split(' ')
.filter(token => !METAWORDS.includes(token) && token.length > 0)
.map(token => isNaN(token) ? token : toWords(parseInt(token)));
.filter(token => !METAWORDS.includes(token) && token.length > 0);
for (let i = referenceTokens.length - 1; i >= 0; i--) {
if (!isNaN(referenceTokens[i])) {
referenceTokens.push(toWords(parseInt(referenceTokens[i])));
}
}

if (stringTokens.length === 0) {
return false;
Expand All @@ -163,11 +190,14 @@ function stringMatchesReference(string, reference) {
let tokenMatches = false;

for (let j = 0; j < referenceTokens.length; j++) {
let errors = dljs.distance(stringTokens[i], referenceTokens[j]);
let errors = dljs.distance(stemmer(stringTokens[i]), stemmer(referenceTokens[j]));

if (4 * errors <= referenceTokens[j].length) {
// console.log(stringTokens[i], referenceTokens[j]);
if (strictness * errors <= referenceTokens[j].length || referenceTokens[j].includes(stringTokens[i])) {
tokenMatches = true;
break;
} else {
// console.log(errors, stringTokens[j], referenceTokens[j]);
}
}

Expand Down Expand Up @@ -214,7 +244,7 @@ function checkAnswer(answerline, givenAnswer) {
const parsedAnswerline = parseAnswerline(answerline);

for (const answer of parsedAnswerline['reject']) {
if (stringMatchesReference(answer[2], givenAnswer) && stringMatchesReference(givenAnswer, answer[2])) {
if (stringMatchesReference(answer[2], givenAnswer, 7) && stringMatchesReference(givenAnswer, answer[2], 7)) {
return 'reject';
}
}
Expand All @@ -237,6 +267,16 @@ function checkAnswer(answerline, givenAnswer) {
}
}

if (answerline.includes('[prompt on partial') || answerline.includes('(prompt on partial')) {
const [answer1, answer2] = parsedAnswerline.accept[0][0].split(' ');
if (answerWorks(answer1, givenAnswer, isFormattedAnswerline)) {
return 'prompt';
}
if (answerWorks(answer2, givenAnswer, isFormattedAnswerline)) {
return 'prompt';
}
}

return 'reject';
}

Expand Down
64 changes: 61 additions & 3 deletions tests/quizbowl.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,24 @@ const formatted_answers = [
"<b><u>Louis-Philippe</u></b> [or <b><u>Duke d’Orleans</u></b>; prompt on “Citizen King” before mentioned]",
"Johann <b><u>Tserclaes</u></b>, Graf von <b><u>Tilly</u></b> (accept either underlined answer as well as Count of <b><u>Tilly</u></b>)",
"<b><u>Paul</u></b> <b><u>Bäumer</u></b> [accept either name]",
"<b><u>Matsuo</u></b> <b><u>Bashō</u></b> [accept either underlined part; accept <b><u>Matsuo</u></b> Kinsaku or <b><u>Matsuo</u></b> Chūemon Munefusa]"
"<b><u>Matsuo</u></b> <b><u>Bashō</u></b> [accept either underlined part; accept <b><u>Matsuo</u></b> Kinsaku or <b><u>Matsuo</u></b> Chūemon Munefusa]",
"<b><u>Prime Minister</u></b> of <b><u>Australia</u></b> [prompt on partial answers]",
"hypothesis <b><u>test</u></b>",
"<b><u>graphene</u></b> [do not accept or prompt on \"graphite\"]",
"<b><u>amide</u></b>s [do not accept or prompt on \"amines\"]",
"<b><u>cosmic microwave background</u></b> radiation [or <b><u>CMB</u></b>; or <b><u>CMBR</u></b>]",
"<b><u>1980s</u></b> [prompt on <u>80s</u>]",
"<u>working memory</u> [prompt on partial answers or on “short-term memory”]",
"Pyotr Ilyich <b><u>Tchaikovsky</u></b>’s <b><u>Piano Concerto</u></b> No. <b><u>1</u></b> [accept <b><u>Tchaikovsky</u></b>’s <b><u>PianoConcerto</u></b> in <b><u>B-flat</u></b> <b><u>minor</u></b> until “B-flat” is read; accept word forms like <b><u>Tchaikovsky</u></b>’s <b><u>first piano concerto</u></b>; prompt on partial answer]",
];

const answers = [
"Heinrich Böll [or Heinrich Theodor Böll]",
"primatology [or word forms; accept any answers about the study of great apes, nonhuman primates, gorillas, bonobos, or chimpanzees; prompt on the study of monkeys or simians; prompt on word forms of ethology, biology, anthropology, or evolutionary or social psychology; prompt on the study of animals with “what type of animals?”]",
"China [or People’s Republic of China; do not accept or prompt on “Republic of China”]"
"China [or People’s Republic of China; do not accept or prompt on “Republic of China”]",
"amides [do not accept or prompt on \"amines\"]",
"RAF [or Red Army Faction; accept Baader–Meinhof group; accept Baader–Meinhof gang; accept Rote Armee Fraktion] (The Action Directe communiqué was also signed “kommando elisabeth van dyck,” in reference to a fallen member of RAF.)",
"Lenski's longterm E. coli evolution experiment [accept anything mentioning the long term evolution of E. Coli]",
];

const tests = [
Expand Down Expand Up @@ -79,19 +90,66 @@ const tests = [
['accept', formatted_answers[11], 'Matsuo Basho'],
['accept', formatted_answers[11], 'Matsuo Bashō'],

['accept', formatted_answers[12], 'prime minister of australia'],
['accept', formatted_answers[12], 'australia prime minister'],
['accept', formatted_answers[12], 'australian prime minister'],
['prompt', formatted_answers[12], 'prime minister'],

['accept', formatted_answers[13], 'hypothesis testing'],
['accept', formatted_answers[13], 'testing'],
['accept', formatted_answers[13], 'test'],

['accept', formatted_answers[14], 'graphene'],
['reject', formatted_answers[14], 'graphite'],

['accept', formatted_answers[15], 'amides'],
['accept', formatted_answers[15], 'amide'],
['reject', formatted_answers[15], 'amine'],

['accept', formatted_answers[16], 'cosmic microwave background radiation'],
['accept', formatted_answers[16], 'cosmic microwave background'],
['accept', formatted_answers[16], 'cmb'],
['accept', formatted_answers[16], 'cmbr'],

['accept', formatted_answers[17], '1980s'],
['accept', formatted_answers[17], '1980'],
['prompt', formatted_answers[17], '80'],
['prompt', formatted_answers[17], '80s'],
['reject', formatted_answers[17], '90s'],
['reject', formatted_answers[17], '90'],
// ['reject', formatted_answers[17], '1990'], // TODO
// ['reject', formatted_answers[17], '1990s'], // TODO

['accept', formatted_answers[18], 'working memory'],
['prompt', formatted_answers[18], 'memory'],

['accept', formatted_answers[19], 'Tchaikovsky Piano Concerto no 1'],
// ['prompt', formatted_answers[19], 'Piano Concerto'], // TODO

['accept', answers[0], 'boll'],
['accept', answers[0], 'heinrich boll'],
['accept', answers[0], 'Böll'],
['accept', answers[0], 'Heinrich Böll'],

// unformatted answerlines
['reject', answers[1], 'chimp'], // TODO: make this accept
['accept', answers[1], 'chimp'],
['accept', answers[1], 'chimpanzee'],

// reject clauses that are a subset of acceptable answer
['accept', answers[2], 'China'],
['accept', answers[2], 'people’s republic of China'],
['reject', answers[2], 'republic of china'],

['accept', answers[3], 'amides'],
['accept', answers[3], 'amide'],
['reject', answers[3], 'amine'],

['accept', answers[4], 'baader meinhof'],
['accept', answers[4], 'raf'],
['accept', answers[4], 'red army faction'],
['accept', answers[4], 'red army'],

['accept', answers[5], 'lenski long term e coli experiment'],
];

let successful = 0, total = 0;
Expand Down

0 comments on commit f5af784

Please sign in to comment.