Implement leech-detection
John Smith

John Smith commited on 2022-232 00:56:32
Showing 2 changed files, with 37 additions and 3 deletions.


Adds option "l" to the main utility, which sets it to detect
and present leeches found in the active deckfile, measured as prompts
with >= 8 recorded failures/lapses (excluding suspended prompts,
as facilitated in a9f5760), sorted in order of decreasing leech-badness.
... ...
@@ -116,6 +116,13 @@ local function isatty()
116 116
     return succ
117 117
 end
118 118
 
119
+--[[
120
+    return number indicating how much of a leech the prompt is
121
+]]
122
+local function badness(pr)
123
+    return pr.fail / (pr.fail + pr.succ) * math.log(pr.fail)
124
+end
125
+
119 126
 --[[
120 127
     check action based on first command-line argument;
121 128
     if nothing valid given, default to help
... ...
@@ -123,7 +130,7 @@ end
123 130
 local action = nil
124 131
 if arg[1] then
125 132
     local fc = arg[1]:sub(1, 1)
126
-    if fc == "a" or fc == "h" or fc == "r" or fc == "s" then
133
+    if fc == "a" or fc == "h" or fc == "l" or fc == "r" or fc == "s" then
127 134
         action = fc
128 135
     else
129 136
         action = "h"
... ...
@@ -225,9 +232,31 @@ if action == "a" then
225 232
 -- Help
226 233
 elseif action == "h" then
227 234
     io.write(string.format(
228
-        "Usage: %s ACTION\n\nACTION may be one of\n\t(a)dd card\n\t(h)elp\n\t(r)eview session\n\t(s)tatistics\n",
235
+        "Usage: %s ACTION\n\nACTION may be one of\n\t(a)dd card\n\t(h)elp\n\t(l)eech search\n\t(r)eview session\n\t(s)tatistics\n",
229 236
         arg[0]
230 237
     ))
238
+-- Leech search
239
+elseif action == "l" then
240
+    local deck = loader.load(deckfname, config)
241
+    if deck then
242
+        local leechqueue = {}
243
+        for i, cg in ipairs(deck) do
244
+            for j, pr in ipairs(cg) do
245
+                if pr.fail > 8 and pr.delay >= 0 then
246
+                    table.insert(leechqueue, { i, j })
247
+                end
248
+            end
249
+        end
250
+        table.sort(leechqueue, function(a, b)
251
+            return badness(deck[a[1]][a[2]]) > badness(deck[b[1]][b[2]])
252
+        end)
253
+        io.write(string.format("Detected %d leeches:\n", #leechqueue))
254
+        for _, ij in ipairs(leechqueue) do
255
+            io.write(string.format("%s\n", deck[ij[1]][ij[2]]))
256
+        end
257
+    else
258
+        io.write("Failed to load deck\n")
259
+    end
231 260
 -- Review session
232 261
 elseif action == "r" then
233 262
     local deck
... ...
@@ -29,7 +29,12 @@ function Prompt:new(pt, ca, rts, rd, sc, fc, ct, ez)
29 29
 end
30 30
 
31 31
 function Prompt:__tostring()
32
-    return string.format("\"%s\" (\"%s\") %d -> %d+%d*%d %d/%d", self.text, self.ans, self.created, self.time, self.delay, self.ease, self.succ, self.fail)
32
+    return string.format(
33
+        "\"%s\" (\"%s\") %d -> %d+%d*%f %d/%d",
34
+        self.text, self.ans, self.created or 0,
35
+        self.time, math.floor(self.delay), self.ease,
36
+        self.succ, self.fail
37
+    )
33 38
 end
34 39
 
35 40
 --[[
36 41