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;
}
}
}