Class: JLDrill::Strategy

Inherits:
Object
  • Object
show all
Defined in:
lib/jldrill/model/Quiz/Strategy.rb

Overview

Strategy for choosing, promoting and demoting items in the quiz.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(quiz) ⇒ Strategy

Returns a new instance of Strategy.



12
13
14
15
16
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 12

def initialize(quiz)
    @quiz = quiz
    @reviewStats = Statistics.new(quiz, 4)
    @forgottenStats = Statistics.new(quiz, 5)
end

Instance Attribute Details

#forgottenStatsObject (readonly)

Returns the value of attribute forgottenStats.



10
11
12
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 10

def forgottenStats
  @forgottenStats
end

#reviewStatsObject (readonly)

Returns the value of attribute reviewStats.



10
11
12
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 10

def reviewStats
  @reviewStats
end

Class Method Details

.forgottenSetBinObject



30
31
32
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 30

def Strategy.forgottenSetBin
    return 5
end

.newSetBinObject



18
19
20
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 18

def Strategy.newSetBin
    return 0
end

.reviewSetBinObject



26
27
28
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 26

def Strategy.reviewSetBin
    return 4
end

.workingSetBinsObject



22
23
24
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 22

def Strategy.workingSetBins
    return 1..3
end

Instance Method Details

#contentsObject

Returns the contents for the quiz



50
51
52
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 50

def contents
    @quiz.contents
end

#correct(item) ⇒ Object

Mark the item as having been reviewed correctly



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 300

def correct(item)
    @reviewStats.correct(item)
    @forgottenStats.correct(item)
    item.itemStats.correct
    if ((item.bin == Strategy.reviewSetBin) ||
        (item.bin == Strategy.forgottenSetBin))
        item.schedule.correct
        promote(item)
    else
        item.allCorrect
        if(item.schedule.score >= options.promoteThresh)
            promote(item)
        end
    end
end

#createProblem(item) ⇒ Object

Create a problem for the given item at the correct level



247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 247

def createProblem(item)
    item.itemStats.createProblem
    @reviewStats.startTimer(item.bin == Strategy.reviewSetBin)
    @forgottenStats.startTimer(item.bin == Strategy.forgottenSetBin)
    # Drill at the scheduled level in the review and forgotten sets 
    if (item.bin == Strategy.reviewSetBin) ||
        (item.bin == Strategy.forgottenSetBin) 
        problem = item.problem
    else
        # Otherwise drill for the specific bin
        level = item.bin - 1
        problem = ProblemFactory.create(level, item)
    end
    return problem
end

#demote(item) ⇒ Object

Demote the item



286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 286

def demote(item)
    if !item.nil?
        item.demoteAll
        if (item.bin != 0)
            contents.moveToBin(item, 1)
        else
        	# Demoting bin 0 items is non-sensical, but it should do
         # something sensible anyway.
            contents.moveToBin(item, 0)
        end
    end
end

#findUnseen(binNum) ⇒ Object

Return the index of the first item in the bin that hasn’t been seen yet. If they have all been seen, reset the bin



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 161

def findUnseen(binNum)
    bin = contents.bins[binNum]
    if bin.empty?
        return -1
    end

    if bin.allSeen?
        bin.setUnseen
    end
    bin.firstUnseen            
end

#forgottenSetObject



86
87
88
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 86

def forgottenSet
    @quiz.contents.bins[Strategy.forgottenSetBin]
end

#forgottenSetSizeObject



90
91
92
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 90

def forgottenSetSize
    forgottenSet.length
end

#getForgottenItemObject



213
214
215
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 213

def getForgottenItem
    forgottenSet[0]
end

#getItemObject

Get an item to quiz



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 223

def getItem
    item = nil
    if contents.empty?
        return nil
    end

    if !workingSetFull?
        if shouldReview?
            item = getReviewItem
        elsif !forgottenSet.empty?
            item = getForgottenItem
        elsif !newSet.empty?
            item = getNewItem
        end
    end

    # Usually we get a working item if the above is not true
    item = getWorkingItem if item.nil?

    item.allSeen(true)
    return item
end

#getNewItemObject

Get an item from the New Set



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 190

def getNewItem
    if options.randomOrder
        index = rand(newSet.length)
    else
        index = findUnseen(Strategy.newSetBin)
    end
    if !(index == -1)
        item = newSet[index]
        # Resetting the schedule to make up for the consequences
        # of an old bug where reset drills weren't reset properly.
        item.resetSchedules
        promote(item)
        item
    else
        nil
    end
end

#getReviewItemObject

Get an item from the Review Set



209
210
211
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 209

def getReviewItem
    reviewSet[0]
end

#getWorkingItemObject

Get an item from the Working Set



218
219
220
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 218

def getWorkingItem
    randomUnseen(Strategy.workingSetBins)
end

#incorrect(item) ⇒ Object

Mark the item as having been reviewed incorrectly



317
318
319
320
321
322
323
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 317

def incorrect(item)
    @reviewStats.incorrect(item)
    @forgottenStats.incorrect(item)
    item.allIncorrect
    item.itemStats.incorrect
    demote(item)
end

#learn(item) ⇒ Object

Promote the item from the working set into the review set without any further training. If it is already in the review set, simply mark it correct.



328
329
330
331
332
333
334
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 328

def learn(item)
    if item.bin <= 3
        item.setScores(options.promoteThresh)
        contents.moveToBin(item, 3)
    end
    correct(item)
end

#newSetObject



59
60
61
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 59

def newSet
    @quiz.contents.bins[Strategy.newSetBin]
end

#optionsObject

Returns the options for the quiz



55
56
57
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 55

def options
    @quiz.options
end

#promote(item) ⇒ Object

Promote the item to the next level/bin



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 264

def promote(item)
    if !item.nil?
        item.setScores(0)
        if item.bin < 3
            item.setLevels(item.bin)
            contents.moveToBin(item, item.bin + 1)
        else
            if item.bin == 3
                # Newly promoted items
                item.itemStats.consecutive = 1
                @reviewStats.learned += 1
                @forgottenStats.learned += 1
                item.scheduleAll
            end
            # Put the item at the back of the bin
            contents.bins[item.bin].delete(item)
            reviewSet.push(item)
        end
    end
end

#randomUnseen(range) ⇒ Object

Returns a random unseen item. Return nil if the range is empty. Resets the seen status if all the items are already seen.



175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 175

def randomUnseen(range)
    if contents.rangeEmpty?(range)
        return nil
    end
    if contents.rangeAllSeen?(range)
        range.to_a.each do |bin|
            contents.bins[bin].setUnseen
        end
    end
    index = rand(contents.numUnseen(range))
    item = contents.findUnseen(index, range)
    item
end

#rescheduleObject

Sort the items according to their schedule



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 95

def reschedule
    # Check for legacy files that may have Kanji 
    # problems schedules but no kanji
    reviewSet.each do |x|
        x.removeInvalidKanjiProblems
    end
    # Sort the review set
    reviewSet.sort! do |x,y|
        x.schedule.reviewLoad <=> y.schedule.reviewLoad
    end
    # Move old items to the forgotten set
    while ((options.forgettingThresh != 0.0) &&
           (!reviewSet.empty?) && 
           (reviewSet[0].schedule.reviewRate >= options.forgettingThresh.to_f))
        contents.moveToBin(reviewSet[0], Strategy.forgottenSetBin)
    end
    # Sort the forgotten set
    forgottenSet.sort! do |x,y|
        x.schedule.reviewLoad <=> y.schedule.reviewLoad
    end
    # If the user changes the settings then we may have to
    # unforget some items
    while ((!forgottenSet.empty?) &&
          ((options.forgettingThresh == 0.0) ||
           (forgottenSet.last.schedule.reviewRate < 
                options.forgettingThresh.to_f)))
        contents.moveToBin(forgottenSet.last, Strategy.reviewSetBin)
    end
    # Sort the review set again
    reviewSet.sort! do |x,y|
        x.schedule.reviewLoad <=> y.schedule.reviewLoad
    end
    
end

#reviewSetObject



77
78
79
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 77

def reviewSet
    @quiz.contents.bins[Strategy.reviewSetBin]
end

#reviewSetKnown?Boolean

Returns true if the review set has been reviewed enough that it is considered to be known. This happens when we have reviewed ten items while in the target zone. The target zone occurs when in the last 10 items, we have a 90% success rate or when we have a 90% confidence that the the items have a 90% chance of success.

Returns:

  • (Boolean)


138
139
140
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 138

def reviewSetKnown?
    !(10 - @reviewStats.timesInTargetZone > 0)        
end

#reviewSetSizeObject

Returns the number of items in the review set



82
83
84
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 82

def reviewSetSize
    reviewSet.length
end

#shouldReview?Boolean

Returns true if at least one working set full of items have been promoted to the review set, and the review set is not known to the required level. Note: if the new set and the working set are both empty, this will always return true.

Returns:

  • (Boolean)


148
149
150
151
152
153
154
155
156
157
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 148

def shouldReview?
    # if we only have review set items, or we are in review mode
    # then return true
    if  (newSet.empty? && workingSetEmpty?) || (options.reviewMode)
        return true
    end
    
    !reviewSetKnown? && (reviewSetSize >= options.introThresh) && 
        !(reviewSet.allSeen?)
end

#statusObject

Returns a string showing the status of the quiz with this strategy



35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 35

def status
    if shouldReview?
        retVal = "     #{@reviewStats.recentAccuracy}%"
        if @reviewStats.inTargetZone?
            retVal += " - #{(10 - @reviewStats.timesInTargetZone)}"
        end
    elsif !forgottenSet.empty?
        retVal = " Forgotten Items"
    else
        retVal = "     New Items"
    end
    retVal
end

#workingSetEmpty?Boolean

Returns:

  • (Boolean)


63
64
65
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 63

def workingSetEmpty?
    contents.rangeEmpty?(Strategy.workingSetBins)
end

#workingSetFull?Boolean

Returns true if the working set is not full

Returns:

  • (Boolean)


73
74
75
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 73

def workingSetFull?
     workingSetSize >= options.introThresh
end

#workingSetSizeObject

Returns the number of items in the working set



68
69
70
# File 'lib/jldrill/model/Quiz/Strategy.rb', line 68

def workingSetSize
    contents.bins[1].length + contents.bins[2].length + contents.bins[3].length
end