using System.Collections.Generic; using System.Linq; using EscapeRoomEngine.Engine.Runtime.Modules.Description; using EscapeRoomEngine.Engine.Runtime.UI; using EscapeRoomEngine.Engine.Runtime.Utilities; using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger; using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType; namespace EscapeRoomEngine.Engine.Runtime.Measurements { /// /// This class is the main way to interact with measurements. /// public static class Measure { /// /// Whether to store the taken measurements in the database. Disable for test runs. /// public static bool Store { get; set; } public static bool PreferLessPlayed => GameControl.Instance.preferLessPlayed.Value; public static int PuzzlesSolved { get; private set; } private static Dictionary _runningMeasurements; private static Session _currentSession; /// /// The average time to solve a specific puzzle. /// public static float AverageTime(PuzzleModuleDescription puzzle) => PuzzleStorage.Instance.LoadOrNew(puzzle).AverageTimeToSolve; /// /// The average time to solve a group of puzzles. /// public static float AverageTime(IEnumerable puzzles) => puzzles.Sum(AverageTime); /// /// The average time to solve a specific room. /// /// This only counts puzzles already placed in a room. public static float AverageTime(Room room) => room.puzzles.Sum(puzzle => AverageTime((PuzzleModuleDescription)puzzle.description)); /// /// Estimate the time a specific puzzle will take to be solved by the current player. /// public static float EstimateTime(PuzzleModuleDescription puzzle) => PuzzleStorage.Instance.LoadOrNew(puzzle).EstimateTimeToSolve(SessionPercentile()); /// /// Estimate the time a group of puzzles will take to be solved by the current player. /// public static float EstimateTime(IEnumerable puzzles) => puzzles.Sum(EstimateTime); /// /// Estimate the time a room will take to be solved by the current player. /// /// This only counts puzzles already placed in a room. public static float EstimateTime(Room room) => room.puzzles.Sum(puzzle => EstimateTime((PuzzleModuleDescription)puzzle.description)); /// /// How often a specific puzzle has been played before. /// public static int TimesPlayed(PuzzleModuleDescription puzzle) => PuzzleStorage.Instance.LoadOrNew(puzzle).Measurements.Count; /// /// The estimated percentile the current player is placed in. /// public static float SessionPercentile() => _currentSession?.MeanPercentile ?? 0.5f; /// /// Start counting the time to solve a specific puzzle. /// public static void StartMeasuring(PuzzleModuleDescription puzzle) { _runningMeasurements[puzzle.Id] = new PuzzleMeasurement { TimeStarted = GameControl.Instance.TimeElapsed, TimeSolved = GameControl.Instance.TimeElapsed }; Logger.Log($"Started measuring {puzzle}", LogType.Measuring); } /// /// A puzzle has been solved and the measurement should be stopped. This will also store the finished measurement in the database. /// public static void Solve(PuzzleModuleDescription puzzle) { if (_currentSession == null) { throw new EngineException("Measuring session must be started before taking measurements."); } var measurement = _runningMeasurements[puzzle.Id]; if (Store) { PuzzleStorage.Instance.EndMeasurement(_currentSession, puzzle, measurement); } _runningMeasurements.Remove(puzzle.Id); PuzzlesSolved++; Logger.Log($"Solved {puzzle} with measurement {measurement}", LogType.Measuring); } /// /// Start a new session and place the current player at the 50th percentile. /// public static void StartSession() { _currentSession = new Session(); // add a first plan result with the initial target and estimated times _currentSession.PlanResults.Add(new PlanResult( GameControl.Instance.TargetTime, .5f, GameControl.Instance.EstimatedTime)); Logger.Log($"Started {_currentSession}", LogType.Measuring); } /// /// End a session and store it in the database. /// public static void EndSession(float time) { if (Store) { PuzzleStorage.Instance.EndSession(_currentSession, time); } Logger.Log($"Ended {_currentSession}", LogType.Measuring); } public static void Clear() { _runningMeasurements = new Dictionary(); Store = true; } } }