Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Assets/Art/Icons/refresh.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
168 changes: 168 additions & 0 deletions Assets/Art/Icons/refresh.png.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 2 additions & 12 deletions Assets/Scripts/Domain/BoardGeneration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@

public static class BoardGeneration
{
/// <summary>
/// Default cap kept for API backward compatibility. The greedy walk does not use it.
/// </summary>
private const int DefaultDeadEndLimit = 10;
private const int MinArrowLength = 2;

/// <summary>
Expand Down Expand Up @@ -57,12 +53,7 @@ public void EnsureCapacity(int requiredWords)
/// </summary>
public static readonly object FinalizationMarker = new object();

public static IEnumerator FillBoardIncremental(
Board board,
int maxLength,
Random random,
int deadEndLimit = DefaultDeadEndLimit
)
public static IEnumerator FillBoardIncremental(Board board, int maxLength, Random random)
{
board.InitializeForGeneration();
int maxPossibleArrows = board.Width * board.Height / 2;
Expand Down Expand Up @@ -95,8 +86,7 @@ public static bool GenerateArrows(
int maxLength,
int amount,
Random random,
out int createdArrows,
int deadEndLimit = DefaultDeadEndLimit
out int createdArrows
)
{
createdArrows = 0;
Expand Down
18 changes: 17 additions & 1 deletion Assets/Scripts/Domain/Models/ReplayData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public double ComputedSolveElapsed
{
double elapsed = 0.0;
DateTime checkpoint = DateTime.MinValue;
DateTime lastEventTime = DateTime.MinValue;
bool paused = false;

foreach (var evt in events)
{
Expand All @@ -66,14 +68,28 @@ public double ComputedSolveElapsed
switch (evt.type)
{
case ReplayEventType.StartSolve:
checkpoint = ts;
paused = false;
break;
case ReplayEventType.SessionRejoin:
if (!paused && checkpoint != DateTime.MinValue)
{
// Orphan rejoin: accumulate up to last event, skip the gap.
elapsed += (lastEventTime - checkpoint).TotalSeconds;
}
checkpoint = ts;
paused = false;
break;
case ReplayEventType.SessionLeave:
case ReplayEventType.EndSolve:
elapsed += (ts - checkpoint).TotalSeconds;
paused = true;
break;
case ReplayEventType.EndSolve:
if (!paused)
elapsed += (ts - checkpoint).TotalSeconds;
break;
}
lastEventTime = ts;
}

return elapsed;
Expand Down
30 changes: 24 additions & 6 deletions Assets/Scripts/Domain/ReplayPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,20 @@ public ReplayPlayer(ReplayData data)
}

if (startTime == DateTime.MinValue && _data.events.Count > 0)
{
startTime = DateTime.Parse(_data.events[0].timestamp).ToUniversalTime();
}

// We use the solve elapsed computation approach: track active time excluding pauses
double activeTime = 0;
DateTime checkpoint = startTime;
bool tracking = false;
// If there's no StartSolve event, start tracking from the beginning
bool tracking = (
startTime != DateTime.MinValue
&& !_data.events.Exists(e => e.type == ReplayEventType.StartSolve)
);
bool paused = false; // true between session_leave and session_rejoin
DateTime lastEventTime = startTime;

foreach (var evt in _data.events)
{
Expand All @@ -61,13 +69,24 @@ public ReplayPlayer(ReplayData data)
case ReplayEventType.StartSolve:
checkpoint = ts;
tracking = true;
paused = false;
break;
case ReplayEventType.SessionRejoin:
if (!paused && tracking)
{
// Orphan rejoin (no preceding session_leave). The player
// force-quit and resumed. Treat the previous event's time
// as the implicit leave — accumulate active time up to that
// point, excluding the gap between then and this rejoin.
activeTime += (lastEventTime - checkpoint).TotalSeconds;
}
checkpoint = ts;
paused = false;
break;
case ReplayEventType.SessionLeave:
if (tracking)
activeTime += (ts - checkpoint).TotalSeconds;
paused = true;
break;
case ReplayEventType.Clear:
case ReplayEventType.Reject:
Expand All @@ -83,19 +102,18 @@ public ReplayPlayer(ReplayData data)
activeTime += (ts - checkpoint).TotalSeconds;
break;
}
lastEventTime = ts;
}

_eventTimes = times.ToArray();
// Shift all event times by lead-in so early clears don't fire at t=0
for (int i = 0; i < _eventTimes.Length; i++)
_eventTimes[i] += LeadInSeconds;

double lastEventRaw =
_eventTimes.Length > 0 ? _eventTimes[_eventTimes.Length - 1] - LeadInSeconds : 0;
double rawDuration =
_data.finalTime >= 0
? _data.finalTime
: (
_eventTimes.Length > 0 ? _eventTimes[_eventTimes.Length - 1] - LeadInSeconds : 0
);
_data.finalTime >= 0 ? Math.Max(_data.finalTime, lastEventRaw) : lastEventRaw;
_totalDuration = rawDuration + LeadInSeconds + ExitPaddingSeconds;
}

Expand Down
15 changes: 15 additions & 0 deletions Assets/Scripts/Domain/ReplayRecorder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ public void RecordSessionLeave()

public void RecordSessionRejoin()
{
// If the previous session didn't end with a session_leave (e.g. force-quit),
// inject one using the timestamp of the last recorded event to avoid
// orphan rejoins that break time computation.
if (_events.Count > 0 && _events[_events.Count - 1].type != ReplayEventType.SessionLeave)
{
_events.Add(
new ReplayEvent
{
seq = _nextSeq++,
type = ReplayEventType.SessionLeave,
timestamp = _events[_events.Count - 1].timestamp,
}
);
}

_events.Add(
new ReplayEvent
{
Expand Down
Loading
Loading