Redesign the algorithm to choose wrongly sorted elements

- When checking the order the first time, elements above are preferred marked as wrong.
- When the user corrects his solution, the newest inserted elements are preferred marked as wrong.

This affects only cases, where there are multiple options for the same number of correct elements.
In general, the algorithm minimizes the number of elements it needs to mark as wrong.

Closes #5
This commit is contained in:
Lukas Fürderer 2020-09-04 15:55:46 +02:00
parent fc7f630001
commit 099cc72429

93
quiz.js
View File

@ -133,47 +133,52 @@ function shuffle_cards(section) {
return a; return a;
} }
function choose_incorrects(idx_vals) { function choose_incorrects(elems) {
let options = { let options = {
0: [], "-1": [],
}; };
function find_longest_chain(maxkey) { function chain_is_better(old_chain, new_chain) {
let longest = null; if (old_chain == null) {
for (let key in options) { return true;
if ( } else if (new_chain.length > old_chain.length) {
( return true;
maxkey == null } else if (new_chain.length < old_chain.length) {
|| key <= maxkey return false;
) }
&& ( let old_nums = old_chain.map(elem => elem[1]).sort((a, b) => a - b).reverse();
longest == null let new_nums = new_chain.map(elem => elem[1]).sort((a, b) => a - b).reverse();
|| options[key].length > options[longest].length for (let i=0; i<old_nums.length; i++) {
|| ( if (new_nums[i] < old_nums[i]) {
options[key].length == options[longest].length return true;
&& Number.parseInt(key) < Number.parseInt(longest) } else if (new_nums[i] > old_nums[i]) {
) return false;
)
) {
longest = key;
} }
} }
return longest;
} }
idx_vals.forEach((val) => { function build_chain(elem) {
let longest = find_longest_chain(val); let best = null;
let mychain = options[longest].slice(); for (let chain_end in options) {
mychain.push(val); let chain = options[chain_end];
options[val + 1] = mychain; chain_end = Number.parseInt(chain_end);
}); if (elem == null || chain_end < elem[0]) {
let result_chain = find_longest_chain(null); let newchain = chain.slice();
let a = []; if (elem != null) {
for (let i=0; i<cards_content[0].length; i++) { newchain.push(elem);
a.push(true); }
if (chain_is_better(best, newchain)) {
best = newchain;
}
}
}
return best;
} }
options[result_chain].forEach((elem) => { elems.forEach(elem => {
a[elem] = false; let chain = build_chain(elem);
options[chain[chain.length - 1][0]] = chain;
}); });
return idx_vals.map(idx => a[idx]); let result_chain = build_chain(null);
let corrects = result_chain.map(elem => elem[0]);
return elems.map(elem => !corrects.includes(elem[0]));
} }
var app = new Vue({ var app = new Vue({
@ -185,6 +190,7 @@ var app = new Vue({
selected: null, selected: null,
items: shuffle_cards(0), items: shuffle_cards(0),
}, },
next_insert_num: null,
solution: [], solution: [],
correction_visible: false, correction_visible: false,
}, },
@ -274,8 +280,8 @@ var app = new Vue({
return this.solution.map(elem => false); return this.solution.map(elem => false);
} }
if (this.store.section == 0) { if (this.store.section == 0) {
let idx_vals = this.solution.map(elem => elem.idx[0]); let elems = this.solution.map(elem => [elem.idx[0], elem.insert_num]);
return choose_incorrects(idx_vals); return choose_incorrects(elems);
} else { } else {
return this.solution.map((elem, i) => { return this.solution.map((elem, i) => {
return elem.idx.length > this.store.section return elem.idx.length > this.store.section
@ -316,11 +322,18 @@ var app = new Vue({
store_card_visible: function(idx) { store_card_visible: function(idx) {
return !this.solution.some(solution => solution.idx[this.store.section] == idx); return !this.solution.some(solution => solution.idx[this.store.section] == idx);
}, },
calculate_insert_num: function() {
if (this.next_insert_num == null) {
return null;
}
return this.next_insert_num++;
},
store_select: function(i) { store_select: function(i) {
if (this.solution.length == 0) { if (this.solution.length == 0) {
this.solution.push({ this.solution.push({
idx: [i], idx: [i],
got_revealed: false, got_revealed: false,
insert_num: this.calculate_insert_num(),
}); });
} else if (i == this.store.selected) { } else if (i == this.store.selected) {
this.store.selected = null; this.store.selected = null;
@ -335,6 +348,7 @@ var app = new Vue({
this.solution.splice(0, 0, { this.solution.splice(0, 0, {
idx: [this.store.selected], idx: [this.store.selected],
got_revealed: false, got_revealed: false,
insert_num: this.calculate_insert_num(),
}); });
this.store.selected = null; this.store.selected = null;
}, },
@ -349,6 +363,7 @@ var app = new Vue({
this.solution.splice(this.find_solution(elem)+1, 0, { this.solution.splice(this.find_solution(elem)+1, 0, {
idx: [this.store.selected], idx: [this.store.selected],
got_revealed: false, got_revealed: false,
insert_num: this.calculate_insert_num(),
}); });
this.store.selected = null; this.store.selected = null;
}, },
@ -360,6 +375,12 @@ var app = new Vue({
} }
}, },
show_correction: function() { show_correction: function() {
if (this.store.section == 0) {
this.solution.forEach((elem, i) => {
elem.insert_num = -i;
});
this.next_insert_num = 1;
}
this.correction_visible = true; this.correction_visible = true;
}, },
open_next_column: function() { open_next_column: function() {