public override Card GetNextCard(TrialPerformance trialPerformance) { InitCardDataIfNeeded(); if (_previousCardIntervalData != null) { RecordPreviousTrial(trialPerformance); } if (_activeCards.Count <= 0 && _inactiveCards.Count <= 0) { return(null); } if (ShouldActivateNewCard()) // TODO Should this really happen if the user fails? Should we have a staleness metric like back in the good old days? { ActivateNewCard(); } Card nextCard = _activeCards[0].card; _previousCardIntervalData = _activeCards[0]; _activeCards.Remove(_previousCardIntervalData); // TODO slow? return(nextCard); }
private void RecordPreviousTrial(TrialPerformance trialPerformance) { if (_previousCardIntervalData == null) { throw new InvalidOperationException("Tried to record trial data without first getting a card. GetNextCard first!"); } _previousCardIntervalData = CalculateCardIntervalData(_previousCardIntervalData.card); // HACK HACK FIXME TODO does this even work? int insertAt = 0; for (int i = 0; i < _activeCards.Count; i++) { insertAt = i + 1; // TODO FIXME This is a bug that nicely allows us to not see failed cards twice in a row but schedules things off by one if (_previousCardIntervalData.interval >= _activeCards[i].interval) { continue; } else { break; } } _activeCards.Insert(insertAt, _previousCardIntervalData); _database.AddHistoryEntry(_previousCardIntervalData.card, trialPerformance); }
private void InitCardDataIfNeeded() { if (_inactiveCards != null && _activeCards != null) { return; } List <AnkiCardIntervalData> cardsWithIntervals = new List <AnkiCardIntervalData>(); foreach (Card c in _cards) { AnkiCardIntervalData cardIntervalData = CalculateCardIntervalData(c); cardsWithIntervals.Add(cardIntervalData); } cardsWithIntervals.Sort((cardA, cardB) => cardB.interval.CompareTo(cardA.interval)); _inactiveCards = cardsWithIntervals; _activeCards = new List <AnkiCardIntervalData>(); for (int i = _inactiveCards.Count - 1; i >= 0; i--) { if (_inactiveCards[i].interval > 0) { _activeCards.Add(_inactiveCards[i]); _inactiveCards.RemoveAt(i); } } }
private AnkiCardIntervalData CalculateCardIntervalData(Card c) { IEnumerable <CardHistoryEntry> history = c.HistoryEntries; history = history.OrderBy(x => x.EntryTime); double uf = 0; double interval = 0; CardHistoryEntry lastHistoryEntry = null; foreach (CardHistoryEntry historyEntry in history) { if (lastHistoryEntry == null) { interval = newInterval; lastHistoryEntry = historyEntry; } switch (historyEntry.TrialPerformance) { case TrialPerformance.Fail: uf = CalculateFailFactor(uf); interval = interval * newInterval; break; case TrialPerformance.Easy: uf = CalculateEasyFactor(uf); interval = CalculateEasyInterval(lastHistoryEntry, historyEntry, interval, uf); break; case TrialPerformance.Normal: uf = CalculateNormalFactor(uf); interval = CalculateNormalInterval(lastHistoryEntry, historyEntry, interval, uf); break; case TrialPerformance.Hard: uf = CalculateHardFactor(uf); interval = CalculateHardInterval(lastHistoryEntry, historyEntry, interval); break; default: throw new InvalidDataBaseOperationException(String.Format("Unsupported TrialPerformance value in card history: {0}", historyEntry.TrialPerformance)); } lastHistoryEntry = historyEntry; } var cardIntervalData = new AnkiCardIntervalData() { lastReview = lastHistoryEntry?.EntryTime, card = c, interval = interval, understandingFactor = uf }; return(cardIntervalData); }