comment pass

This commit is contained in:
2022-12-29 16:16:49 +01:00
parent 643e03192a
commit ff01a700bd
75 changed files with 634 additions and 65 deletions

View File

@@ -9,6 +9,9 @@ using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
namespace EscapeRoomEngine.Engine.Runtime.Measurements
{
/// <summary>
/// This class is the main way to interact with measurements.
/// </summary>
public static class Measure
{
/// <summary>
@@ -19,20 +22,46 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
private static Dictionary<int, PuzzleMeasurement> _runningMeasurements;
private static Session _currentSession;
/// <summary>
/// The average time to solve a specific puzzle.
/// </summary>
public static float AverageTime(PuzzleModuleDescription puzzle) =>
PuzzleStorage.Instance.Load(puzzle).AverageTimeToSolve;
/// <summary>
/// The average time to solve a group of puzzles.
/// </summary>
public static float AverageTime(IEnumerable<PuzzleModuleDescription> puzzles) => puzzles.Sum(AverageTime);
/// <summary>
/// The average time to solve a specific room.
/// </summary>
/// <remarks>This only counts puzzles already placed in a room.</remarks>
public static float AverageTime(Room room) =>
room.puzzles.Sum(puzzle => AverageTime((PuzzleModuleDescription)puzzle.description));
/// <summary>
/// Estimate the time a specific puzzle will take to be solved by the current player.
/// </summary>
public static float EstimateTime(PuzzleModuleDescription puzzle) =>
PuzzleStorage.Instance.Load(puzzle).EstimateTimeToSolve(SessionPercentile());
/// <summary>
/// Estimate the time a group of puzzles will take to be solved by the current player.
/// </summary>
public static float EstimateTime(IEnumerable<PuzzleModuleDescription> puzzles) => puzzles.Sum(EstimateTime);
/// <summary>
/// Estimate the time a room will take to be solved by the current player.
/// </summary>
/// <remarks>This only counts puzzles already placed in a room.</remarks>
public static float EstimateTime(Room room) =>
room.puzzles.Sum(puzzle => EstimateTime((PuzzleModuleDescription)puzzle.description));
/// <summary>
/// The estimated percentile the current player is placed in.
/// </summary>
public static float SessionPercentile() => _currentSession?.MeanPercentile ?? 0.5f;
/// <summary>
/// Start counting the time to solve a specific puzzle.
/// </summary>
public static void StartMeasuring(PuzzleModuleDescription puzzle)
{
_runningMeasurements[puzzle.Id] = new PuzzleMeasurement
@@ -44,6 +73,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
Logger.Log($"Started measuring {puzzle}", LogType.Measuring);
}
/// <summary>
/// A puzzle has been solved and the measurement should be stopped. This will also store the finished measurement in the database.
/// </summary>
public static void Solve(PuzzleModuleDescription puzzle)
{
if (_currentSession == null)
@@ -62,6 +94,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
Logger.Log($"Solved {puzzle} with measurement {measurement}", LogType.Measuring);
}
/// <summary>
/// Start a new session and place the current player at the 50th percentile.
/// </summary>
public static void StartSession()
{
_currentSession = new Session();
@@ -75,6 +110,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
Logger.Log($"Started {_currentSession}", LogType.Measuring);
}
/// <summary>
/// End a session and store it in the database.
/// </summary>
public static void EndSession(float time)
{
if (Store)

View File

@@ -3,10 +3,22 @@ using Realms;
namespace EscapeRoomEngine.Engine.Runtime.Measurements
{
/// <summary>
/// The target and estimated time, along with the percentile since the last section, that will be stored in the database.
/// </summary>
public class PlanResult : EmbeddedObject
{
/// <summary>
/// The target time set by the game master when this section ended.
/// </summary>
public float TargetTime { get; set; }
/// <summary>
/// The percentile of the current player during this section.
/// </summary>
public float SectionPercentile { get; set; }
/// <summary>
/// The estimated time to complete the whole puzzle plan when this section ended.
/// </summary>
public float TimeEstimation { get; set; }
[UsedImplicitly]

View File

@@ -8,15 +8,30 @@ using Realms;
namespace EscapeRoomEngine.Engine.Runtime.Measurements
{
/// <summary>
/// The representation of a specific puzzle in the database. This includes all measurements for this puzzle.
/// </summary>
[SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")]
public class Puzzle : RealmObject
{
[PrimaryKey]
public int ID { get; set; }
/// <summary>
/// All puzzle measurements recorded for this puzzle.
/// </summary>
public IList<PuzzleMeasurement> Measurements { get; }
/// <summary>
/// How much time has been spent on this puzzle in total. This is used to calculate the average time to solve this puzzle.
/// </summary>
public float TotalTimeSpentOnPuzzle => Measurements.Sum(measurement => measurement.Time);
/// <summary>
/// The average time to solve this puzzle using all measurements recorded for it.
/// </summary>
public float AverageTimeToSolve => Measurements.Count > 0 ? TotalTimeSpentOnPuzzle / Measurements.Count : 0f;
/// <summary>
/// The normal distribution of all recorded measurements.
/// </summary>
public NormalDistribution Distribution => new(TimesAsSamples());
[UsedImplicitly]

View File

@@ -3,11 +3,23 @@ using Realms;
namespace EscapeRoomEngine.Engine.Runtime.Measurements
{
/// <summary>
/// A single measurement, consisting of when a puzzle was started and solved, stored in the database.
/// </summary>
public class PuzzleMeasurement : EmbeddedObject
{
/// <summary>
/// The relative time since the engine was initialised of when this puzzle was started.
/// </summary>
public float TimeStarted { get; set; }
/// <summary>
/// The relative time since the engine was initialised of when this puzzle was solved.
/// </summary>
public float TimeSolved { get; set; }
/// <summary>
/// The total time taken to solve this puzzle during this measurement.
/// </summary>
public float Time => TimeSolved - TimeStarted;
public override string ToString()

View File

@@ -7,12 +7,22 @@ using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType;
namespace EscapeRoomEngine.Engine.Runtime.Measurements
{
/// <summary>
/// The storage engine that manages the data in a realm database.
/// </summary>
public class PuzzleStorage : MonoBehaviour
{
/// <summary>
/// The schema version must be updated whenever the schema in the database changed.
/// </summary>
private const int SchemaVersion = 2;
/// <summary>
/// The active instance of the storage engine.
/// </summary>
public static PuzzleStorage Instance { get; private set; }
[Tooltip("The path of the database relative to where the engine stores persistent data.")]
[SerializeField]
private string databasePath = "measurements.realm";
@@ -20,6 +30,7 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
private void OnEnable()
{
// configure special actions during specific migrations of the database
var config = new RealmConfiguration
{
SchemaVersion = SchemaVersion,
@@ -59,6 +70,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
#region Session
/// <summary>
/// End a session and store it to the database.
/// </summary>
public void EndSession(Session session, float time)
{
session.Time = time;
@@ -70,6 +84,10 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
#region Puzzles
/// <summary>
/// Create a new puzzle for a specific description and store it in the database.
/// </summary>
/// <remarks>This requires that the puzzle does not yet exist in the database.</remarks>
private Puzzle New(PuzzleModuleDescription puzzle)
{
Puzzle created = null;
@@ -79,10 +97,20 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
return created;
}
/// <summary>
/// Load a specific puzzle from the database or create it if it wasn't found.
/// </summary>
private Puzzle LoadOrNew(PuzzleModuleDescription puzzle) => _realm.Find<Puzzle>(puzzle.Id) ?? New(puzzle);
/// <summary>
/// Load a specific puzzle from the database.
/// </summary>
/// <returns>The puzzle or null if it wasn't found in the database.</returns>
public Puzzle Load(PuzzleModuleDescription puzzle) => _realm.Find<Puzzle>(puzzle.Id);
/// <summary>
/// Delete all records concerning a specific puzzle from the database irreversibly.
/// </summary>
public void Delete(PuzzleModuleDescription puzzle)
{
var found = Load(puzzle);
@@ -92,6 +120,9 @@ namespace EscapeRoomEngine.Engine.Runtime.Measurements
}
}
/// <summary>
/// End the measurement for a specific puzzle and store it in the database. Also stores the results of the current puzzle plan.
/// </summary>
public void EndMeasurement(Session session, PuzzleModuleDescription puzzle, PuzzleMeasurement measurement)
{
var found = LoadOrNew(puzzle);

View File

@@ -8,16 +8,33 @@ using Realms;
namespace EscapeRoomEngine.Engine.Runtime.Measurements
{
/// <summary>
/// The representation of a session in the database. This stores all plan results during this session and what puzzles were solved.
/// </summary>
[SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")]
public class Session : RealmObject
{
[PrimaryKey]
public ObjectId ID { get; set; }
public float Time { get; set; }
/// <summary>
/// The plan results for each section during this section.
/// </summary>
/// <remarks>The order of the plan results corresponds with the order of the solved puzzles.</remarks>
public IList<PlanResult> PlanResults { get; }
/// <summary>
/// The puzzles solved during this session.
/// </summary>
/// <remarks>The order of the solved puzzles corresponds with the order of the plan results.</remarks>
public IList<Puzzle> PuzzlesSolved { get; }
/// <summary>
/// The section percentiles the current player has been placed in.
/// </summary>
public IEnumerable<float> Percentiles => PlanResults.Select(result => result.SectionPercentile);
/// <summary>
/// The average of all section percentiles.
/// </summary>
public float MeanPercentile => PlanResults.Count == 0 ? .5f : Probability.Mean(Percentiles.ToArray());
[UsedImplicitly]