public static void AddAttempt(Guid cardId, bool result) { // scoring double score = 0d; DateTime now = DateTime.Now; Attempt lastAttempt = new Attempt(); using (CitizenEntities db = new CitizenEntities()) { if (db.Attempts.Where(a => a.Card.CardId == cardId).Count() > 0) { lastAttempt = db.Attempts.Where(a => a.Card.CardId == cardId).OrderByDescending(a => a.CreateDate).First(); } else lastAttempt = null; if (lastAttempt != null) { double minSinceLast = (now - lastAttempt.CreateDate).TotalMinutes; if (minSinceLast <= 0) throw new Exception("New attempt CreateDate happened before last attempt. Impossible!!"); if (result) score = 1d - (1d / (Math.Log(minSinceLast + 9d, 10d))); else score = -(1d / (Math.Log(minSinceLast + 9d, 10d))); } // update card, and insert new attempt. Card card = db.Cards.Where(c => c.CardId == cardId).First(); if (score > 0d) card.Score = Math.Max(card.Score, score); if (score < 0d) card.Score = Math.Min(card.Score, score); else card.Score = 0d; card.Attempts.Add(new Attempt() { AttemptId = Guid.NewGuid(), Card = card, CreateDate = now, Result = result }); db.SaveChanges(); } }
public static Card GetNextCard() { // first fetch: // - have attempt(s), but less than 3; and // - no attempt in last 1 min; and // - not in last 5 attempts using (CitizenEntities db = new CitizenEntities()) { List<Guid> temp1 = (from a in db.Attempts group a by a.Card.CardId into g where g.Count() > 0 && g.Count() < 4 select g.Key).ToList(); if (temp1.Count != 0) { DateTime oneMinBack = DateTime.Now.AddMinutes(-1); List<Guid> temp2 = (from a in db.Attempts where a.CreateDate > oneMinBack select a.Card.CardId).ToList(); List<Guid> temp3 = (from a in db.Attempts orderby a.CreateDate descending select a.Card.CardId).Take(5).ToList(); List<Guid> temp4 = (from t in temp1 where !temp2.Contains(t) && !temp3.Contains(t) select t).ToList(); if (temp4.Count != 0) { Random ran = new Random(); Guid picked = temp4[ran.Next(temp4.Count)]; return db.Cards.Where(c => c.CardId == picked).First(); } } // second fetch: // - no attempt in last 5 mins // - no frequent correct ones // - has 2+ correct attempts in the past 1 day; or // - has 5+ correct attempts in the past 1 week // - if last attempt correct, pick from score<=0; otherwise, pick from score>0. // - if no last attempt, do not pay attention to score. // - if found, pick the one has min(|score|) DateTime now = DateTime.Now; DateTime fiveMinBack = now.AddMinutes(-5); DateTime oneDayBack = now.AddDays(-1); DateTime oneWeekBack = now.AddDays(-7); List<Card> temp5 = (from c in db.Cards where c.Attempts.Where(a => a.CreateDate > fiveMinBack).Count() == 0 select c).ToList(); if (temp5.Count == 0) return null; List<Guid> frequentCorrectDay = (from a in db.Attempts where a.CreateDate > oneDayBack && a.Result == true group a by a.Card.CardId into g where g.Count() >= 2 select g.Key).ToList(); List<Guid> frequentCorrectWeek = (from a in db.Attempts where a.CreateDate > oneWeekBack && a.Result == true group a by a.Card.CardId into g where g.Count() >= 5 select g.Key).ToList(); temp5 = temp5.Where(c => !frequentCorrectDay.Contains(c.CardId) && !frequentCorrectWeek.Contains(c.CardId)).ToList(); Attempt lastAttempt = new Attempt(); if (db.Attempts.Count() != 0) lastAttempt = db.Attempts.OrderByDescending(a => a.CreateDate).First(); else lastAttempt = null; List<Card> temp6 = new List<Card>(); if (lastAttempt != null) { if (lastAttempt.Result) temp6 = temp5.Where(c => c.Score <= 0).ToList(); else temp6 = temp5.Where(c => c.Score > 0).ToList(); } if (temp6.Count == 0) temp6 = temp5; return (from t in temp6 orderby Math.Abs(t.Score) select t).First(); } }