public async Task <QuizQuestion> BuildQuestionForVerseAsync(BiblePathsCoreDbContext context, BibleVerse verse, int MaxPoints, string BibleId) { int BlankWordProbability = 3; // read as 1 in every 3 valid words may get blanked rnd is 0 based int MinPoints = 3; int Iteration = 0; int MaxIterations = 3; string BlankWordSring = "_____"; string FitBPrepend = "fill in the blanks: "; int BlankedWordCount = 0; List <string> BlankedWords = new List <string>(); string QuestionString = String.Empty; BibleId = await QuizQuestion.GetValidBibleIdAsync(context, BibleId); Random rnd = new Random(); // We'll make at most MaxIteratons at this, increasing BlankWordProbability each time. while (Iteration < MaxIterations && BlankedWordCount < MinPoints) { QuestionString = verse.Text; BlankedWords.Clear(); BlankedWordCount = 0; // Read the verse "word" by "word" i.e. stop at each Space till we hit the end. int i = 0; while (i < QuestionString.Length && BlankedWordCount < MaxPoints) { int WordStart = i; int WordEnd = QuestionString.IndexOf(" ", i); if (WordEnd == -1) { WordEnd = QuestionString.Length - 1; } // Now we should have a rough "word" to work with, // but let's progressively shrink our Word Selction until it's starts/ends with a letter. if (WordStart < WordEnd && WordEnd < QuestionString.Length) { // find the first letter while (WordStart < WordEnd && !char.IsLetter(QuestionString, WordStart)) { WordStart++; } // find the last letter while (WordEnd > WordStart && !char.IsLetter(QuestionString, WordEnd)) { WordEnd--; } // Now we should have a true "word" at least as far as we are concerned. string Word = QuestionString.Substring(WordStart, (WordEnd + 1) - WordStart); if (await IsWordGoodForBlankAsync(context, Word, BibleId)) { // Ok now we don't want to simply replace every valid word so let's get random int dice = rnd.Next(BlankWordProbability); if (dice == 0) // 0 will always turn up on the 3rd iteration. { // Blank out our word in the QuestionString StringBuilder VerseWithBlanksSB = new StringBuilder(); VerseWithBlanksSB.Append(QuestionString.Substring(0, WordStart)); VerseWithBlanksSB.Append(BlankWordSring); VerseWithBlanksSB.Append(QuestionString.Substring(WordEnd + 1)); QuestionString = VerseWithBlanksSB.ToString(); // Add our word to the blanked words list BlankedWords.Add(Word); BlankedWordCount++; // Set our index to the latest instance of BlankWordString i = QuestionString.LastIndexOf(BlankWordSring) + BlankWordSring.Length; } else { i = WordEnd + 1; } } else { i = WordEnd + 1; } } else { // Why would we ever be here? i = WordEnd + 1; } } Iteration++; BlankWordProbability--; // Reducing this increases the probability of any word being selected. } QuizQuestion NewQuestion = new QuizQuestion(); QuizAnswer FitBAnswer = new QuizAnswer(); NewQuestion.BibleId = BibleId; NewQuestion.Points = BlankedWordCount; NewQuestion.Question = FitBPrepend + QuestionString; NewQuestion.BookNumber = verse.BookNumber; NewQuestion.Chapter = verse.Chapter; NewQuestion.StartVerse = verse.Verse; NewQuestion.EndVerse = verse.Verse; NewQuestion.Source = "BiblePaths.Net Question Generator - Iteration: " + Iteration.ToString(); // Build the Answer FitBAnswer.Answer = string.Join(", ", BlankedWords); NewQuestion.QuizAnswers.Add(FitBAnswer); if (NewQuestion.QuizAnswers.Count > 0) { NewQuestion.IsAnswered = true; } else { NewQuestion.IsAnswered = false; } return(NewQuestion); }
public async Task <QuizQuestion> GetNextQuizQuestionFromTemplateAsync(BiblePathsCoreDbContext context, string bibleId) { QuizQuestion ReturnQuestion = new QuizQuestion(); PredefinedQuiz Template = new PredefinedQuiz(); try { Template = await context.PredefinedQuizzes.Include(T => T.PredefinedQuizQuestions).Where(T => T.Id == PredefinedQuiz).FirstAsync(); } catch { // This is the couldn't find template scenario, ReturnQuestion.QuestionSelected = false; return(ReturnQuestion); } // Ok we've got our Template now which Book/Chapter do we want. int QuestionNumber = QuestionsAsked + 1; int TemplateQuestionNumber = QuestionNumber; // We need to calculate a Template Number if (QuestionNumber > Template.NumQuestions) { TemplateQuestionNumber = QuestionNumber % Template.NumQuestions; if (TemplateQuestionNumber == 0) { TemplateQuestionNumber = Template.NumQuestions; } } // It is actually OK to not find a Question Object, we just treat that as the random book scenario. PredefinedQuizQuestion TemplateQuestion = new PredefinedQuizQuestion(); try { TemplateQuestion = Template.PredefinedQuizQuestions.Where(Q => Q.QuestionNumber == TemplateQuestionNumber).First(); } catch { // This is the more common pick a random Book Scenario. if (Template.BookNumber >= Bible.MinBookListID) { // This is the BookList Scenario return(await GetNextQuizQuestionFromBookListAsync(context, bibleId, Template.BookNumber)); } else { // this is the Book scenario. return(await GetNextQuizQuestionFromBookAsync(context, bibleId, Template.BookNumber)); } } if (TemplateQuestion.BookNumber == 0) { // This is the pick a random Book Scenario, we're unlikely to hit this, more often the question object won't exist at all. if (Template.BookNumber >= Bible.MinBookListID) { // This is the BookList Scenario return(await GetNextQuizQuestionFromBookListAsync(context, bibleId, Template.BookNumber)); } else { // this is the Book scenario. return(await GetNextQuizQuestionFromBookAsync(context, bibleId, Template.BookNumber)); } } else { // This would be the selected Book Scenario, but do we have a selected Chapter? if (TemplateQuestion.Chapter == 0) { // this is the selected book but random Chapter scenario return(await GetNextQuizQuestionFromBookAsync(context, bibleId, TemplateQuestion.BookNumber)); } else { // this is the selected Book and Chapter scenario return(await GetNextQuizQuestionFromBookAndChapterAsync(context, bibleId, TemplateQuestion.BookNumber, TemplateQuestion.Chapter)); } } }