private List <MiniGameData> selectMiniGames(int nMinigamesToSelect) { List <MiniGameData> newPlaySessionMiniGames = selectMiniGamesForCurrentPlaySession(nMinigamesToSelect); if (ConfigAI.VerboseTeacher) { var debugString = ""; debugString += ConfigAI.FormatTeacherReportHeader("Minigames Selected"); foreach (var minigame in newPlaySessionMiniGames) { debugString += "\n" + minigame.Code; } Debug.Log(debugString); } return(newPlaySessionMiniGames); }
public void LoadCurrentPlaySessionData(string currentPlaySessionId) { var pos = new JourneyPosition(currentPlaySessionId); currentJourneyContents = progressionContents.GetContentsUpToPlaySession(pos); currentPlaySessionContents = progressionContents.GetContentsOfPlaySession(pos); currentLearningBlockContents = progressionContents.GetContentsOfLearningBlock(pos); currentStageContents = progressionContents.GetContentsOfStage(pos); if (ConfigAI.VerbosePlaySessionInitialisation) { string debugString = ""; debugString += ConfigAI.FormatTeacherReportHeader("Play Session Initalisation (" + currentPlaySessionId + ")"); debugString += "\n Current PS:\n" + currentPlaySessionContents; debugString += "\n Current LB:\n" + currentLearningBlockContents; debugString += "\n Current ST:\n" + currentStageContents; debugString += "\n Current journey:\n" + currentJourneyContents; debugString += "\n Whole contents:\n" + progressionContents.AllContents; ConfigAI.AppendToTeacherReport(debugString); } }
private List <T> WeightedDataSelect <T>(List <T> source_data_list, int nToSelect, SelectionSeverity severity) where T : IData { VocabularyDataType dataType = VocabularyDataType.Letter; if (typeof(T) == typeof(LetterData)) { dataType = VocabularyDataType.Letter; } else if (typeof(T) == typeof(WordData)) { dataType = VocabularyDataType.Word; } else if (typeof(T) == typeof(PhraseData)) { dataType = VocabularyDataType.Phrase; } // Given a (filtered) list of data, select some using weights var score_data_list = dbManager.Query <VocabularyScoreData>("SELECT * FROM " + typeof(VocabularyScoreData).Name + " WHERE VocabularyDataType = '" + (int)dataType + "'"); if (ConfigAI.VerboseDataSelection) { weightedData_debugString += ConfigAI.FormatTeacherReportHeader("Selection Weights"); weightedData_debugString = ""; } var weights_list = new List <float>(); foreach (var sourceData in source_data_list) { float cumulativeWeight = 0; if (ConfigAI.VerboseDataSelection) { weightedData_debugString += "\n" + sourceData.GetId() + " ---"; } // Get score data var score_data = score_data_list.Find(x => x.ElementId == sourceData.GetId()); float currentScore = 0; int daysSinceLastScore = 0; if (score_data != null) { var timespanFromLastScoreToNow = GenericHelper.GetTimeSpanBetween(score_data.UpdateTimestamp, GenericHelper.GetTimestampForNow()); daysSinceLastScore = timespanFromLastScoreToNow.Days; currentScore = score_data.Score; } //UnityEngine.Debug.Log("Data " + id + " score: " + currentScore + " days " + daysSinceLastScore); // Score Weight [0,1]: higher the lower the score [-1,1] is var scoreWeight = 0.5f * (1 - currentScore); cumulativeWeight += scoreWeight * ConfigAI.Vocabulary_Score_Weight; if (ConfigAI.VerboseDataSelection) { weightedData_debugString += " \tScore: " + scoreWeight * ConfigAI.Vocabulary_Score_Weight + "(" + scoreWeight + ")"; } // RecentPlay Weight [1,0]: higher the more in the past we saw that data const float dayLinerWeightDecrease = 1f / ConfigAI.DaysForMaximumRecentPlayMalus; float weightMalus = daysSinceLastScore * dayLinerWeightDecrease; float recentPlayWeight = 1f - UnityEngine.Mathf.Min(1, weightMalus); cumulativeWeight += recentPlayWeight * ConfigAI.Vocabulary_RecentPlay_Weight; if (ConfigAI.VerboseDataSelection) { weightedData_debugString += " \tRecent: " + recentPlayWeight * ConfigAI.Vocabulary_RecentPlay_Weight + "(" + recentPlayWeight + ")"; } // Current focus weight [1,0]: higher if the data is part of the current play session / learning block / stage float currentPlaySessionWeight = 0; if (currentPlaySessionContents.Contains(sourceData)) { currentPlaySessionWeight = 1; } else if (currentLearningBlockContents.Contains(sourceData)) { currentPlaySessionWeight = 0.5f; } else if (currentStageContents.Contains(sourceData)) { currentPlaySessionWeight = 0.2f; } cumulativeWeight += currentPlaySessionWeight * ConfigAI.Vocabulary_CurrentPlaySession_Weight; if (ConfigAI.VerboseDataSelection) { weightedData_debugString += " \tFocus: " + currentPlaySessionWeight * ConfigAI.Vocabulary_CurrentPlaySession_Weight + "(" + currentPlaySessionWeight + ")"; } // If the cumulative weight goes to the negatives, we give it a fixed weight // TODO check if we shound use if (cumulativeWeight <= ConfigAI.Vocabulary_MinTotal_Weight) // TODO check the "continue" because it wont' save the data if (cumulativeWeight <= 0) { cumulativeWeight = ConfigAI.Vocabulary_MinTotal_Weight; continue; } // Save cumulative weight weights_list.Add(cumulativeWeight); if (ConfigAI.VerboseDataSelection) { weightedData_debugString += " TOTw: " + cumulativeWeight; } } //return source_data_list; if (ConfigAI.VerboseDataSelection) { ConfigAI.AppendToTeacherReport(weightedData_debugString); } // Select data from the list var selected_data_list = new List <T>(); if (source_data_list.Count > 0) { int nToSelectFromCurrentList = 0; List <T> chosenData = null; switch (severity) { case SelectionSeverity.AsManyAsPossible: case SelectionSeverity.AllRequired: nToSelectFromCurrentList = UnityEngine.Mathf.Min(source_data_list.Count, nToSelect); chosenData = RandomHelper.RouletteSelectNonRepeating(source_data_list, weights_list, nToSelectFromCurrentList); selected_data_list.AddRange(chosenData); break; case SelectionSeverity.MayRepeatIfNotEnough: int nRemainingToSelect = nToSelect; while (nRemainingToSelect > 0) { var listCopy = new List <T>(source_data_list); nToSelectFromCurrentList = UnityEngine.Mathf.Min(source_data_list.Count, nRemainingToSelect); chosenData = RandomHelper.RouletteSelectNonRepeating(listCopy, weights_list, nToSelectFromCurrentList); selected_data_list.AddRange(chosenData); nRemainingToSelect -= nToSelectFromCurrentList; } break; } } return(selected_data_list); }
public List <T> SelectData <T>(System.Func <List <T> > builderSelectionFunction, SelectionParameters selectionParams, bool isTest = false, bool canReturnZero = false) where T : IVocabularyData { // skip if we require 0 values if (selectionParams.nRequired == 0 && !selectionParams.getMaxData) { return(new List <T>()); } if (ConfigAI.VerboseDataSelection) { debugString_selectData = ""; debugString_selectData += ConfigAI.FormatTeacherReportHeader("Data Selection: " + typeof(T).Name); } // (1) Filtering based on the builder's logic var dataList = builderSelectionFunction(); int nAfterBuilder = dataList.Count; if (ConfigAI.VerboseDataFiltering) { debugString_selectData += ("\n Builder: " + dataList.Count); } // (2) Filtering based on journey if (selectionParams.useJourney && !ConfigAI.ForceJourneyIgnore) { switch (selectionParams.journeyFilter) { case SelectionParameters.JourneyFilter.CurrentJourney: dataList = dataList.FindAll(x => currentJourneyContents.Contains(x)); break; case SelectionParameters.JourneyFilter.UpToFullCurrentStage: dataList = dataList.FindAll(x => currentJourneyContents.Contains(x) || currentStageContents.Contains(x)); break; case SelectionParameters.JourneyFilter.CurrentLearningBlockOnly: dataList = dataList.FindAll(x => currentLearningBlockContents.Contains(x)); break; } } if (selectionParams.severity == SelectionSeverity.AllRequired) { if (!CheckRequiredNumberReached(dataList, selectionParams, nAfterBuilder)) { throw new Exception("The teacher could not find " + selectionParams.nRequired + " data instances after applying the journey logic."); } } if (ConfigAI.VerboseDataFiltering) { debugString_selectData += ("\n Journey: " + dataList.Count); } //Debug.Log("Journey: " + dataList.ToDebugStringNewline()); // (3) Filtering based on pack-list history switch (selectionParams.packListHistory) { case PackListHistory.NoFilter: // we do not care which are picked, in this case break; case PackListHistory.ForceAllDifferent: // filter only by those that have not been found already in this pack, if possible dataList = dataList.FindAll(x => !selectionParams.filteringIds.Contains(x.GetId())); if (!CheckRequiredNumberReached(dataList, selectionParams, nAfterBuilder)) { UnityEngine.Debug.Log(debugString_selectData); throw new System.Exception("The teacher could not find " + selectionParams.nRequired + " data instances after applying the pack-history logic."); } break; case PackListHistory.RepeatWhenFull: // reset the previous pack list if needed var tmpDataList = dataList.FindAll(x => !selectionParams.filteringIds.Contains(x.GetId())); if (tmpDataList.Count < selectionParams.nRequired) { // reset and re-pick selectionParams.filteringIds.Clear(); dataList = dataList.FindAll(x => !selectionParams.filteringIds.Contains(x.GetId())); } else { dataList = tmpDataList; } break; } if (ConfigAI.VerboseDataFiltering) { debugString_selectData += ("\n History: " + dataList.Count); } //Debug.Log("History: " + dataList.ToDebugStringNewline()); //if(selectionParams.filteringIds != null) Debug.Log("Filtered ids:: " + selectionParams.filteringIds.ToDebugStringNewline()); // (4) Priority filtering based on current focus List <T> priorityFilteredList = dataList; if (!isTest && !selectionParams.getMaxData && selectionParams.priorityFilter != SelectionParameters.PriorityFilter.NoPriority) { HashSet <T> priorityFilteredHash = new HashSet <T>(); string s = ConfigAI.FormatTeacherReportHeader("Priority Filtering"); switch (selectionParams.priorityFilter) { case SelectionParameters.PriorityFilter.CurrentThenExpand: { int nBefore = selectionParams.nRequired; int nRemaining = selectionParams.nRequired; AddToHashSetFilteringByContents(currentPlaySessionContents, dataList, priorityFilteredHash, ref nRemaining); s += "\n Required: " + nRemaining + " " + typeof(T).Name.ToString(); s += "\n" + (nBefore - nRemaining) + " from PS"; if (nRemaining > 0) { nBefore = nRemaining; AddToHashSetFilteringByContents(currentLearningBlockContents, dataList, priorityFilteredHash, ref nRemaining); s += "\n" + (nBefore - nRemaining) + " from LB"; } if (nRemaining > 0) { nBefore = nRemaining; AddToHashSetFilteringByContents(currentStageContents, dataList, priorityFilteredHash, ref nRemaining); s += "\n" + (nBefore - nRemaining) + " from ST"; } if (nRemaining > 0) { nBefore = nRemaining; AddToHashSetFilteringByContents(currentJourneyContents, dataList, priorityFilteredHash, ref nRemaining); s += "\n" + (nBefore - nRemaining) + " from the current Journey"; } // @note: when journey filtering is disabled, we may still have to get some data from the rest of the journey if (nRemaining > 0 && !selectionParams.useJourney) { nBefore = nRemaining; AddToHashSetFilteringByContents(progressionContents.AllContents, dataList, priorityFilteredHash, ref nRemaining); s += "\n" + (nBefore - nRemaining) + " from the complete contents."; } } break; case SelectionParameters.PriorityFilter.CurrentThenPast: { int nBefore = selectionParams.nRequired; int nRemaining = selectionParams.nRequired; var loopPos = AppManager.I.Player.CurrentJourneyPosition; s += "\n Required: " + nRemaining + " " + typeof(T).Name.ToString(); while (nRemaining > 0) { var loopContents = progressionContents.GetContentsOfPlaySession(loopPos); AddToHashSetFilteringByContents(loopContents, dataList, priorityFilteredHash, ref nRemaining); s += "\n" + (nBefore - nRemaining) + " from PS " + loopPos.ToDisplayedString(true); if (loopPos.Equals(JourneyPosition.InitialJourneyPosition)) { break; } loopPos = AppManager.I.JourneyHelper.FindPreviousJourneyPosition(loopPos); } } break; default: throw new ArgumentOutOfRangeException(); } if (ConfigAI.VerboseDataFiltering) { ConfigAI.AppendToTeacherReport(s); } priorityFilteredList = new List <T>(); priorityFilteredList.AddRange(priorityFilteredHash); if (ConfigAI.VerboseDataFiltering) { debugString_selectData += ("\n Priority: " + priorityFilteredList.Count); } } //Debug.Log("Priority: " + priorityFilteredList.ToDebugStringNewline()); // (5) Weighted selection on the remaining number List <T> selectedList = null; if (selectionParams.getMaxData) { selectedList = priorityFilteredList; } else { selectedList = WeightedDataSelect(priorityFilteredList, selectionParams.nRequired, selectionParams.severity); } if (ConfigAI.VerboseDataFiltering) { debugString_selectData += ("\n Selection: " + selectedList.Count); } //if (selectedList.Count > 0) Debug.Log("Selection: " + selectedList.ToDebugStringNewline()); if (ConfigAI.VerboseDataFiltering && !isTest) { foreach (var selectedEntry in selectedList) { debugString_selectData += " [" + selectedEntry + "]"; } ConfigAI.AppendToTeacherReport(debugString_selectData); } if (selectedList.Count == 0) { if (canReturnZero) { return(selectedList); } throw new System.Exception("The teacher could not find any data with the current filters. The game does not seem to be playable at the selected play session." + "\n" + debugString_selectData); } // Update the filtering ids if (selectionParams.packListHistory != PackListHistory.NoFilter) { selectionParams.filteringIds.AddRange(selectedList.ConvertAll <string>(x => x.GetId()).ToArray()); } // Reorder the selected data based on intrinsic difficulty if (selectionParams.sortDataByDifficulty) { selectedList.Sort((x, y) => (int)(x.GetIntrinsicDifficulty() - y.GetIntrinsicDifficulty())); } return(selectedList); }
private List <MiniGameData> PerformSelection_Random(PlaySessionData playSessionData, int numberToSelect) { // Get all minigames ids for the given playsession (from PlaySessionData) // ... also, keep the weights around var minigame_id_list = new List <string>(); var playsession_weights_dict = new Dictionary <MiniGameCode, float>(); foreach (var minigameInPlaySession in playSessionData.Minigames) { minigame_id_list.Add(minigameInPlaySession.MiniGameCode.ToString()); playsession_weights_dict[minigameInPlaySession.MiniGameCode] = minigameInPlaySession.Weight; } // Get all minigame data, filter by availability (from the static DB) var minigame_data_list = dbManager.FindMiniGameData(x => x.Active && minigame_id_list.Contains(x.GetId())); // Create the weights list too var weights_list = new List <float>(minigame_data_list.Count); // Retrieve the current score data (state) for each minigame (from the dynamic DB) var minigame_score_list = dbManager.Query <MiniGameScoreData>("SELECT * FROM " + typeof(MiniGameScoreData).Name); //UnityEngine.Debug.Log("M GAME SCORE LIST: " + minigame_score_list.Count); //foreach(var l in minigame_score_list) UnityEngine.Debug.Log(l.ElementId); // Determine the final weight for each minigame var required_minigames = new List <MiniGameData>(); string debugString = ConfigAI.FormatTeacherReportHeader("Minigame Selection"); foreach (var minigame_data in minigame_data_list) { float cumulativeWeight = 0; var minigame_scoredata = minigame_score_list.Find(x => x.MiniGameCode == minigame_data.Code); int daysSinceLastScore = 0; if (minigame_scoredata != null) { var timespanFromLastScoreToNow = GenericHelper.GetTimeSpanBetween(minigame_scoredata.UpdateTimestamp, GenericHelper.GetTimestampForNow()); daysSinceLastScore = timespanFromLastScoreToNow.Days; } debugString += minigame_data.Code + " --- \t"; // PlaySession Weight [0,1] float playSessionWeight = playsession_weights_dict[minigame_data.Code] / 100f; // [0-100] cumulativeWeight += playSessionWeight * ConfigAI.MiniGame_PlaySession_Weight; debugString += " PSw: " + playSessionWeight * ConfigAI.MiniGame_PlaySession_Weight + "(" + playSessionWeight + ")"; // Some minigames are required to appear (weight 100+) if (playsession_weights_dict[minigame_data.Code] >= 100) { required_minigames.Add(minigame_data); debugString += " REQUIRED!\n"; continue; } // RecentPlay Weight [1,0] const float dayLinerWeightDecrease = 1f / ConfigAI.DaysForMaximumRecentPlayMalus; float weightMalus = daysSinceLastScore * dayLinerWeightDecrease; float recentPlayWeight = 1f - UnityEngine.Mathf.Min(1, weightMalus); cumulativeWeight += recentPlayWeight * ConfigAI.MiniGame_RecentPlay_Weight; debugString += " RPw: " + recentPlayWeight * ConfigAI.MiniGame_RecentPlay_Weight + "(" + recentPlayWeight + ")"; // Save cumulative weight weights_list.Add(cumulativeWeight); debugString += " TOTw: " + cumulativeWeight + "\n"; } if (ConfigAI.VerboseMinigameSelection) { ConfigAI.AppendToTeacherReport(debugString); } // Number checks int actualNumberToSelect = UnityEngine.Mathf.Min(numberToSelect, minigame_data_list.Count); // Remove the required ones actualNumberToSelect -= required_minigames.Count; foreach (var requiredMinigame in required_minigames) { minigame_data_list.Remove(requiredMinigame); } if (actualNumberToSelect > 0 && minigame_data_list.Count == 0) { throw new System.Exception("Cannot find even a single minigame for play session " + playSessionData.Id); } if (actualNumberToSelect > minigame_data_list.Count) { UnityEngine.Debug.LogWarning("Could not select the requested number of " + numberToSelect + " minigames for play session " + playSessionData.Id + " (only " + minigame_data_list.Count + " are available)"); } // Choose N minigames based on these weights var selectedMiniGameData = RandomHelper.RouletteSelectNonRepeating(minigame_data_list, weights_list, actualNumberToSelect); // Output var finalList = new List <MiniGameData>(); finalList.AddRange(required_minigames); finalList.AddRange(selectedMiniGameData); return(finalList); }