Jldrill Git C0 Coverage Information - RCov

lib/jldrill/model/Quiz/Strategy.rb

Name Total Lines Lines of Code Total Coverage Code Coverage
lib/jldrill/model/Quiz/Strategy.rb 330 232
90.00%
85.78%

Key

Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.

Coverage Details

1 require 'jldrill/model/Quiz/Statistics'
2 require 'jldrill/model/problems/ProblemFactory'
3 
4 module JLDrill
5 
6     # Strategy for choosing, promoting and demoting items in
7     # the quiz.
8     class Strategy
9         attr_reader :stats
10     
11         def initialize(quiz)
12             @quiz = quiz
13             @stats = Statistics.new(quiz)
14         end
15         
16         def Strategy.newSetBin
17             return 0
18         end
19 
20         def Strategy.workingSetBins
21             return 1..3
22         end
23 
24         def Strategy.reviewSetBin
25             return 4
26         end
27 
28         def Strategy.forgottenSetBin
29             return 5
30         end
31         
32         # Returns a string showing the status of the quiz with this strategy
33         def status
34             if shouldReview?
35                 retVal = "     #{@stats.recentAccuracy}%"
36                 if @stats.inTargetZone?
37                     retVal += " - #{(10 - @stats.timesInTargetZone)}"
38                 end
39             elsif !forgottenSet.empty?
40                 retVal = " Forgotten Items"
41             else
42                 retVal = "     New Items"
43             end
44             retVal
45         end
46 
47         # Returns the contents for the quiz
48         def contents
49             @quiz.contents
50         end
51 
52         # Returns the options for the quiz
53         def options
54             @quiz.options
55         end
56 
57         def newSet
58             @quiz.contents.bins[Strategy.newSetBin]
59         end
60         
61         def workingSetEmpty?
62             contents.rangeEmpty?(Strategy.workingSetBins)
63         end
64         
65         # Returns the number of items in the working set
66         def workingSetSize
67             contents.bins[1].length + contents.bins[2].length + contents.bins[3].length
68         end
69         
70         # Returns true if the working set is not full
71         def workingSetFull?
72              workingSetSize >= options.introThresh
73         end
74         
75         def reviewSet
76             @quiz.contents.bins[Strategy.reviewSetBin]
77         end
78 
79         # Returns the number of items in the review set
80         def reviewSetSize
81             reviewSet.length
82         end
83 
84         def forgottenSet
85             @quiz.contents.bins[Strategy.forgottenSetBin]
86         end
87 
88         def forgottenSetSize
89             forgottenSet.length
90         end
91 
92         # Sort the items according to their schedule
93         def reschedule
94             # Check for legacy files that may have Kanji 
95             # problems schedules but no kanji
96             reviewSet.each do |x|
97                 x.removeInvalidKanjiProblems
98             end
99             # Sort the review set
100             reviewSet.sort! do |x,y|
101                 x.schedule.reviewLoad <=> y.schedule.reviewLoad
102             end
103             # Move old items to the forgotten set
104             while ((options.forgettingThresh != 0.0) &&
105                    (!reviewSet.empty?) && 
106                    (reviewSet[0].schedule.reviewRate >= options.forgettingThresh.to_f))
107                 contents.moveToBin(reviewSet[0], Strategy.forgottenSetBin)
108             end
109             # Sort the forgotten set
110             forgottenSet.sort! do |x,y|
111                 x.schedule.reviewLoad <=> y.schedule.reviewLoad
112             end
113             # If the user changes the settings then we may have to
114             # unforget some items
115             while ((!forgottenSet.empty?) &&
116                   ((options.forgettingThresh == 0.0) ||
117                    (forgottenSet.last.schedule.reviewRate < 
118                         options.forgettingThresh.to_f)))
119                 contents.moveToBin(forgottenSet.last, Strategy.reviewSetBin)
120             end
121             # Sort the review set again
122             reviewSet.sort! do |x,y|
123                 x.schedule.reviewLoad <=> y.schedule.reviewLoad
124             end
125             
126         end
127         
128         # Returns true if the review set has been
129         # reviewed enough that it is considered to be
130         # known.  This happens when we have reviewed
131         # ten items while in the target zone.
132         # The target zone occurs when in the last 10
133         # items, we have a 90% success rate or when
134         # we have a 90% confidence that the the items
135         # have a 90% chance of success.
136         def reviewSetKnown?
137             !(10 - @stats.timesInTargetZone > 0)        
138         end
139         
140         # Returns true if at least one working set full of
141         # items have been promoted to the review set, and
142         # the review set is not known to the required
143         # level.
144         # Note: if the new set and the working set are
145         # both empty, this will always return true.
146         def shouldReview?
147             # if we only have review set items, or we are in review mode
148             # then return true
149             if  (newSet.empty? && workingSetEmpty?) || (options.reviewMode)
150                 return true
151             end
152             
153             !reviewSetKnown? && (reviewSetSize >= options.introThresh) && 
154                 !(reviewSet.allSeen?)
155         end
156         
157         # Return the index of the first item in the bin that hasn't been
158         # seen yet.  If they have all been seen, reset the bin
159         def findUnseen(binNum)
160             bin = contents.bins[binNum]
161             if bin.empty?
162                 return -1
163             end
164 
165             if bin.allSeen?
166                 bin.setUnseen
167             end
168             bin.firstUnseen            
169         end
170         
171         # Returns a random unseen item.  Return nil if the range is empty.
172         # Resets the seen status if all the items are already seen.
173         def randomUnseen(range)
174             if contents.rangeEmpty?(range)
175                 return nil
176             end
177             if contents.rangeAllSeen?(range)
178                 range.to_a.each do |bin|
179                     contents.bins[bin].setUnseen
180                 end
181             end
182             index = rand(contents.numUnseen(range))
183             item = contents.findUnseen(index, range)
184             item
185         end
186         
187         # Get an item from the New Set
188         def getNewItem
189             if options.randomOrder
190                 index = rand(newSet.length)
191             else
192                 index = findUnseen(Strategy.newSetBin)
193             end
194             if !(index == -1)
195                 item = newSet[index]
196                 # Resetting the schedule to make up for the consequences
197                 # of an old bug where reset drills weren't reset properly.
198                 item.resetSchedules
199                 promote(item)
200                 item
201             else
202                 nil
203             end
204         end
205         
206         # Get an item from the Review Set
207         def getReviewItem
208             reviewSet[0]
209         end
210 
211         def getForgottenItem
212             forgottenSet[0]
213         end
214         
215         # Get an item from the Working Set
216         def getWorkingItem
217             randomUnseen(Strategy.workingSetBins)
218         end
219 
220         # Get an item to quiz
221         def getItem
222             item = nil
223             if contents.empty?
224                 return nil
225             end
226 
227             if !workingSetFull?
228                 if shouldReview?
229                     item = getReviewItem
230                 elsif !forgottenSet.empty?
231                     item = getForgottenItem
232                 elsif !newSet.empty?
233                     item = getNewItem
234                 end
235             end
236 
237             # Usually we get a working item if the above is not true
238             item = getWorkingItem if item.nil?
239 
240             item.allSeen(true)
241             return item
242         end
243 
244         # Create a problem for the given item at the correct level
245         def createProblem(item)
246             item.itemStats.createProblem
247             @stats.startTimer(item.bin == Strategy.reviewSetBin)
248             # Drill at the scheduled level in the review and forgotten sets 
249             if (item.bin == Strategy.reviewSetBin) ||
250                 (item.bin == Strategy.forgottenSetBin) 
251                 problem = item.problem
252             else
253                 # Otherwise drill for the specific bin
254                 level = item.bin - 1
255                 problem = ProblemFactory.create(level, item)
256             end
257             return problem
258         end
259 
260         # Promote the item to the next level/bin
261         def promote(item)
262             if !item.nil?
263                 item.setScores(0)
264                 if item.bin < 3
265                     item.setLevels(item.bin)
266                     contents.moveToBin(item, item.bin + 1)
267                 else
268                     if item.bin == 3
269                         # Newly promoted items
270                         item.itemStats.consecutive = 1
271                         @stats.learned += 1
272                         item.scheduleAll
273                     end
274                     # Put the item at the back of the bin
275                     contents.bins[item.bin].delete(item)
276                     reviewSet.push(item)
277                 end
278             end
279         end
280 
281         # Demote the item
282         def demote(item)
283             if !item.nil?
284                 item.demoteAll
285                 if (item.bin != 0)
286                     contents.moveToBin(item, 1)
287                 else
288                 	# Demoting bin 0 items is non-sensical, but it should do
289 	                # something sensible anyway.
290                     contents.moveToBin(item, 0)
291                 end
292             end
293         end
294 
295         # Mark the item as having been reviewed correctly
296         def correct(item)
297             @stats.correct(item)
298             item.itemStats.correct
299             if ((item.bin == Strategy.reviewSetBin) ||
300                 (item.bin == Strategy.forgottenSetBin))
301                 item.schedule.correct
302                 promote(item)
303             else
304                 item.allCorrect
305                 if(item.schedule.score >= options.promoteThresh)
306                     promote(item)
307                 end
308             end
309         end
310 
311         # Mark the item as having been reviewed incorrectly
312         def incorrect(item)
313             @stats.incorrect(item)
314             item.allIncorrect
315             item.itemStats.incorrect
316             demote(item)
317         end
318 
319         # Promote the item from the working set into the review
320         # set without any further training.  If it is already
321         # in the review set, simply mark it correct.
322         def learn(item)
323             if item.bin <= 3
324                 item.setScores(options.promoteThresh)
325                 contents.moveToBin(item, 3)
326             end
327             correct(item)
328         end
329     end
330 end

Generated on Mon May 23 16:17:46 +0900 2011 with rcov 0.9.8