Pep95
August 8, 2019, 10:53am
29
So I calculated at what point you will never be able to remove a leech with the given formula.
Since the amount of incorrect times you answered is divided by streak^1.5, there is a maximum streak number that can be made from any given position. This results in that at some point, when you go past a certain amount of mistakes, that item will be a leech forever, until you burn it. Below is a table for every point at which you squash a leech.
Position at which leech will only be removed by burning
Position of Leech when Understood
Point of No Return for Leech Status
Apprentice 1
19 mistakes
Apprentice 2
15 mistakes
Apprentice 3
12 mistakes
Apprentice 4
8 mistakes
Guru 1
6 mistakes
Guru 2
3 mistakes
Master
1 mistake
Enlightened
1 mistake
This also means that to remove a leech that you got wrong the amount given on the right side, you need to get it right more times than the streak you have left at the SRS points on the left.
Since I am of the opinion that this data is also important to the leech list, I thought I might add it myself. It’s not the most beautiful thing, but it gets the job done. On the left side of the slash is the amount of times you got something wrong, on the right side of the slash is your current streak.
EDIT: Added a way to sort the script based on Amount of Errors and Current Streak
EDIT2: Added a way to see if a leech can ever be erased before a burn, if you get everything right up to that burn. I would like to count the amount of impossible items, but I’m not sure how to do that yet.
Edited Script
// ==UserScript==
// @name WaniKani Dashboard Leech List
// @namespace https://www.wanikani.com
// @description Shows top leeches on dashboard (replaces critical items) and all leeches on a dedicated page (replaces critical items)
// @author ukebox
// @version 1.2.3
// @require https://code.jquery.com/jquery-3.3.1.min.js#sha256=FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=
// @include https://www.wanikani.com/dashboard
// @include https://www.wanikani.com/
// @include https://www.wanikani.com/critical-items
// @grant none
// @run-at document-end
// ==/UserScript==
/*
jshint esversion: 6
*/
(function() {
‘use strict’;
let dom = {};
dom.$ = jQuery.noConflict(true);
if (!window.wkof) {
let response = confirm('WaniKani Dashboard Leech List script requires WaniKani Open Framework.\n Click "OK" to be forwarded to installation instructions.');
if (response) {
window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';
}
return;
}
const leechThreshold = 1;
const config = {
wk_items: {
options: {
review_statistics: true,
assignments: true
},
filter: {
level: '1..+0',
srs: '1..8'
}
}
};
window.wkof.include('ItemData');
window.wkof.ready('ItemData').then(getItems).then(determineLeeches).then(updatePage);
function getItems() {
return window.wkof.ItemData.get_items(config);
}
function determineLeeches(items) {
return items.filter(item => isLeech(item));
}
function isLeech(item) {
if (item.review_statistics === undefined) {
return false;
}
let reviewStats = item.review_statistics;
let meaningScore = computeLeechScore(reviewStats.meaning_incorrect, reviewStats.meaning_current_streak);
let readingScore = computeLeechScore(reviewStats.reading_incorrect, reviewStats.reading_current_streak);
item.leech_score = Math.max(meaningScore, readingScore);
if (meaningScore < readingScore) {
item.highestincorrect = reviewStats.reading_incorrect;
item.highestincorrectstreak = reviewStats.reading_current_streak;
} else {
item.highestincorrect = reviewStats.meaning_incorrect;
item.highestincorrectstreak = reviewStats.meaning_current_streak;
}
item.startleech = 9 - (item.assignments.srs_stage - item.highestincorrectstreak);
switch(item.startleech) {
case 8:
if (item.highestincorrect>18) {
item.impossible = "不可能"
} else {
item.impossible = " "
}
break;
case 7:
if (item.highestincorrect>14) {
item.impossible = "不可能"
} else {
item.impossible = " "
}
break;
case 6:
if (item.highestincorrect>11) {
item.impossible = "不可能"
} else {
item.impossible = " "
}
break;
case 5:
if (item.highestincorrect>7) {
item.impossible = "不可能"
} else {
item.impossible = " "
}
break;
case 4:
if (item.highestincorrect>5) {
item.impossible = "不可能"
} else {
item.impossible = " "
}
break;
case 3:
if (item.highestincorrect>2) {
item.impossible = "不可能"
} else {
item.impossible = " "
}
break;
case 2:
if (item.highestincorrect>0) {
item.impossible = "不可能"
} else {
item.impossible = " "
}
break;
case 1:
if (item.highestincorrect>0) {
item.impossible = "不可能"
} else {
item.impossible = " "
}
break;
default:
item.impossible = " "
}
return meaningScore >= leechThreshold || readingScore >= leechThreshold;
}
function computeLeechScore(incorrect, currentStreak) {
return incorrect / Math.pow((currentStreak || 0.5), 1.5);
}
function updatePage(items) {
let is_dashboard = window.location.pathname !== "/critical-items";
if (is_dashboard) {
items = items.sort((a, b) => (b.highestincorrect - a.highestincorrect)||(a.highestincorrectstreak - b.highestincorrectstreak)).slice(0,10);
} else {
items = items.sort((a, b) => (b.highestincorrect - a.highestincorrect)||(a.highestincorrectstreak - b.highestincorrectstreak));
}
console.log(items);
makeLeechList(items, is_dashboard);
}
function round(number, decimals)
{
return +(Math.round(number + "e+" + decimals) + "e-" + decimals);
}
function makeLeechList(items, for_dashboard) {
let rows = "";
items.forEach(item => {
let type = item.assignments.subject_type;
//use slug by default (for kanji and vocab)
let representation = item.data.slug;
//The slug of a radical just has its name, we want the actual symbol.
if (type === 'radical') {
if (item.data.characters) {
//use characters for radicals when possible
representation = item.data.characters;
} else if (item.data.character_images) {
//use SVG image for scalability
let image_data = item.data.character_images.find(x => x.content_type === "image/svg+xml" && x.metadata.inline_styles);
if (image_data) {
representation = `<img style="height: 1em; width: 1em; filter: invert(100%);" src="${image_data.url}" />`;
}
}
}
rows+=`<tr class="${type}"><td><a href="${item.data.document_url}"><span lang="ja">${representation}</span><span class="pull-right"><table style="width:150px"><td width="40%" align="right"> ${item.impossible} </td><td width="30%" align="right">${item.highestincorrect}/${item.highestincorrectstreak}</td><td width="30%" align="right">${round(item.leech_score, 2)}</td></table> </span></a></td></tr>`;
});
let sectionContent = `<h3 class="small-caps">${for_dashboard ? 'Top ' : ''}Leeches</h3>
<table>
<tbody style="display: table-row-group;">
${rows}
</tbody>
</table>
<div class="see-more">
<a class="small-caps" ${for_dashboard ? 'href="critical-items"' : ''}>${for_dashboard ? 'See More Leeches...' : items.length + ' leeches total'}</a>
</div>`;
dom.$('.low-percentage').html(sectionContent);
}
})();