Add "pwc" card type
dkl9

dkl9 commited on 2024-1 10:37:53
Showing 3 changed files, with 84 additions and 15 deletions.


The `CardType` field of a card can now have value "pwc" (rather than
just "bmc" or "seq" as in 44e11a9), indicating that each segment
(separated by double-semicolon, `;;`) should be used as a prompt from
which to recall each other segment that contains a braced answer
(`{x}`). The text before the first `;;` is included in each prompt.

The "a" option (addcards.lua) accepts `pwc` cards, and favours them
over `bmc` or `seq`.
... ...
@@ -6,6 +6,48 @@ local function mismatched(s)
6 6
     return ("}" .. s):find("}[^{]+}") or (s .. "{"):find("{[^}]+{")
7 7
 end
8 8
 
9
+--[[
10
+    returns a Card with note = s, ct autodetected,
11
+    ease = ease
12
+]]
13
+local function parsenote(s, ease)
14
+    local c = Card:new({ note = s, ct = "pwc", created = os.time() })
15
+    local segs = {}
16
+    local n = s:find(";;")
17
+    while n do
18
+        local p = s:find(";;", n + 2)
19
+        local ns = s:sub(n, p)
20
+        table.insert(segs, ns)
21
+        n = p
22
+    end
23
+    for i, ns in ipairs(segs) do
24
+        if ns:match("{[^}]+}") then
25
+            for j = 1, #segs do
26
+                if i ~= j then
27
+                    table.insert(c.pr, Prompt:new({ card = c, ease = ease }))
28
+                end
29
+            end
30
+        end
31
+    end
32
+    if #c.pr >= 1 then
33
+        return c
34
+    end
35
+    c = Card:new({ note = s, ct = "bmc", created = os.time() })
36
+    for _re in s:gmatch("{[^}]+}") do
37
+        table.insert(c.pr, Prompt:new({ card = c, ease = ease }))
38
+    end
39
+    if #c.pr >= 1 then
40
+        return c
41
+    end
42
+    c = Card:new({ note = s, ct = "seq", created = os.time() })
43
+    for _re in s:gmatch(";;") do
44
+        table.insert(c.pr, Prompt:new({ card = c, ease = ease }))
45
+    end
46
+    if #c.pr >= 1 then
47
+        return c
48
+    end
49
+end
50
+
9 51
 local function fn(config, deck)
10 52
     if isatty() then
11 53
         io.write("Reading prompts; press Ctrl-D to stop\n")
... ...
@@ -14,23 +56,11 @@ local function fn(config, deck)
14 56
     local cardctr = 0
15 57
     local prctr = 0
16 58
     for notetext in readtext:gmatch("(..-)\n\n") do
17
-        local newcard = Card:new({ note = notetext, ct = "bmc", created = os.time() })
18
-        for _re in notetext:gmatch("{[^}]+}") do
19
-            table.insert(newcard.pr, Prompt:new({ card = newcard, ease = config:get("StartingEase") }))
20
-        end
21 59
         if mismatched(notetext) then
22 60
             io.write(string.format("Card %d (\"%s\") contains mismatched braces\n", #deck + 1, notetext))
23 61
         end
24
-        if #newcard.pr >= 1 then
25
-            table.insert(deck, newcard)
26
-            cardctr = cardctr + 1
27
-            prctr = prctr + #newcard.pr
28
-        else
29
-            newcard = Card:new({ note = notetext, ct = "seq", created = os.time() })
30
-            for _re in notetext:gmatch(";;") do
31
-                table.insert(newcard.pr, Prompt:new({ card = newcard, ease = config:get("StartingEase") }))
32
-            end
33
-            if #newcard.pr >= 1 then
62
+        local newcard = parsenote(notetext, config:get("StartingEase"))
63
+        if newcard then
34 64
             table.insert(deck, newcard)
35 65
             cardctr = cardctr + 1
36 66
             prctr = prctr + #newcard.pr
... ...
@@ -38,7 +68,6 @@ local function fn(config, deck)
38 68
             io.write(string.format("Card \"%s\" contains no answers (check braces/double-semicolons)\n", notetext))
39 69
         end
40 70
     end
41
-    end
42 71
     io.write(string.format(
43 72
         "Added %d cards (%d prompts)\n",
44 73
         cardctr,
... ...
@@ -50,6 +50,35 @@ function Card:fromrecs(recs, conf)
50 50
                     ctr = ctr + 1
51 51
                 end
52 52
                 table.insert(ret, cc)
53
+            elseif cc.ct == "pwc" then
54
+                local m = cc.note:find(";;")
55
+                local ctr = 1
56
+                local segs = {}
57
+                while m do
58
+                    table.insert(segs, m + 2)
59
+                    m = cc.note:find(";;", m + 2)
60
+                end
61
+                for i, m in ipairs(segs) do
62
+                    local p = cc.note:find(";;", m) or #cc.note
63
+                    local b = cc.note:find("{[^}]+}", m)
64
+                    if b and b < p then
65
+                        for j, n in ipairs(segs) do
66
+                            if i ~= j then
67
+                                table.insert(cc.pr, Prompt:new({
68
+                                    card = cc,
69
+                                    ind = { n, m },
70
+                                    time = tonumber((rec.LastReview or {})[ctr]),
71
+                                    delay = tonumber((rec.LastDelay or {})[ctr]),
72
+                                    succ = tonumber((rec.Successes or {})[ctr]),
73
+                                    fail = tonumber((rec.Failures or {})[ctr]),
74
+                                    ease = tonumber((rec.Ease or {})[ctr]),
75
+                                }))
76
+                                ctr = ctr + 1
77
+                            end
78
+                        end
79
+                    end
80
+                 end
81
+                table.insert(ret, cc)
53 82
             elseif cc.ct == "seq" then
54 83
                 local m, n = cc.note:find(";;")
55 84
                 local ctr = 1
... ...
@@ -38,6 +38,15 @@ function Prompt:text()
38 38
     if self.card.ct == "bmc" then
39 39
         local _, eoa = self.card.note:find("{[^}]+}", self.ind)
40 40
         return (self.card.note:sub(1, self.ind - 1) .. "%A" .. self.card.note:sub(eoa + 1)):gsub("{([^}]+)}", "%1")
41
+    elseif self.card.ct == "pwc" then
42
+        local _, ce = self.card.note:find("^.-;;")
43
+        local qs, as = self.ind[1], self.ind[2]
44
+        local qe = (self.card.note:find(";;", qs) or #self.card.note + 1) - 1
45
+        local ae = (self.card.note:find(";;", as) or #self.card.note + 1) - 1
46
+        print("context is " .. self.card.note:sub(1, ce - 2) .. " from 1 to " .. ce)
47
+        print("question is " .. self.card.note:sub(qs, qe) .. " from " .. qs .. " to " .. qe)
48
+        print("answer is " .. self.card.note:sub(as, ae) .. " from " .. as .. " to " .. ae)
49
+        return self.card.note:sub(1, ce - 2) .. self.card.note:sub(qs, qe):gsub("{([^}]+)}", "%1") .. self.card.note:sub(as, ae):gsub("{([^}]+)}", "%%A")
41 50
     elseif self.card.ct == "seq" then
42 51
         return (self.card.note:sub(1, self.ind - 1) .. "%A"):gsub(";;", "")
43 52
     else
... ...
@@ -51,6 +60,8 @@ function Prompt:ans()
51 60
     end
52 61
     if self.card.ct == "bmc" then
53 62
         return self.card.note:match("{([^}]+)}", self.ind)
63
+    elseif self.card.ct == "pwc" then
64
+        return self.card.note:match("{([^}]+)}", self.ind[2])
54 65
     elseif self.card.ct == "seq" then
55 66
         local m = self.card.note:match("[^;]+;;", self.ind)
56 67
         if not m then m = self.card.note:sub(self.ind) end
57 68