Softly-handle infinite loops in mania beatmap conversion

This commit is contained in:
smoogipoo 2018-08-14 16:40:17 +09:00
parent da01501a1c
commit 2019a89a08
4 changed files with 57 additions and 12 deletions

View File

@ -176,16 +176,23 @@ private Pattern generateRandomHoldNotes(double startTime, int noteCount)
int nextColumn = Random.Next(RandomStart, TotalColumns); int nextColumn = Random.Next(RandomStart, TotalColumns);
for (int i = 0; i < Math.Min(usableColumns, noteCount); i++) for (int i = 0; i < Math.Min(usableColumns, noteCount); i++)
{ {
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column // Find available column
RunWhile(() => pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn), () =>
{
nextColumn = Random.Next(RandomStart, TotalColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
});
addToPattern(pattern, nextColumn, startTime, EndTime); addToPattern(pattern, nextColumn, startTime, EndTime);
} }
// This is can't be combined with the above loop due to RNG // This is can't be combined with the above loop due to RNG
for (int i = 0; i < noteCount - usableColumns; i++) for (int i = 0; i < noteCount - usableColumns; i++)
{ {
while (pattern.ColumnHasObject(nextColumn)) RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
{
nextColumn = Random.Next(RandomStart, TotalColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
});
addToPattern(pattern, nextColumn, startTime, EndTime); addToPattern(pattern, nextColumn, startTime, EndTime);
} }
@ -211,16 +218,21 @@ private Pattern generateRandomNotes(double startTime, int noteCount)
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns) if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{ {
while (PreviousPattern.ColumnHasObject(nextColumn)) RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
{
nextColumn = Random.Next(RandomStart, TotalColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
});
} }
int lastColumn = nextColumn; int lastColumn = nextColumn;
for (int i = 0; i < noteCount; i++) for (int i = 0; i < noteCount; i++)
{ {
addToPattern(pattern, nextColumn, startTime, startTime); addToPattern(pattern, nextColumn, startTime, startTime);
while (nextColumn == lastColumn)
RunWhile(() => nextColumn == lastColumn, () =>
{
nextColumn = Random.Next(RandomStart, TotalColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
});
lastColumn = nextColumn; lastColumn = nextColumn;
startTime += SegmentDuration; startTime += SegmentDuration;
@ -393,14 +405,18 @@ private Pattern generateTiledHoldNotes(double startTime)
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns) if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{ {
while (PreviousPattern.ColumnHasObject(nextColumn)) RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
{
nextColumn = Random.Next(RandomStart, TotalColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
});
} }
for (int i = 0; i < columnRepeat; i++) for (int i = 0; i < columnRepeat; i++)
{ {
while (pattern.ColumnHasObject(nextColumn)) RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
{
nextColumn = Random.Next(RandomStart, TotalColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
});
addToPattern(pattern, nextColumn, startTime, EndTime); addToPattern(pattern, nextColumn, startTime, EndTime);
startTime += SegmentDuration; startTime += SegmentDuration;
@ -427,8 +443,10 @@ private Pattern generateHoldAndNormalNotes(double startTime)
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns) if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{ {
while (PreviousPattern.ColumnHasObject(holdColumn)) RunWhile(() => PreviousPattern.ColumnHasObject(holdColumn), () =>
{
holdColumn = Random.Next(RandomStart, TotalColumns); holdColumn = Random.Next(RandomStart, TotalColumns);
});
} }
// Create the hold note // Create the hold note
@ -455,8 +473,11 @@ private Pattern generateHoldAndNormalNotes(double startTime)
{ {
for (int j = 0; j < noteCount; j++) for (int j = 0; j < noteCount; j++)
{ {
while (rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn) RunWhile(() => rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn, () =>
{
nextColumn = Random.Next(RandomStart, TotalColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
});
addToPattern(rowPattern, nextColumn, startTime, startTime); addToPattern(rowPattern, nextColumn, startTime, startTime);
} }
} }

View File

@ -59,8 +59,10 @@ private int getNextRandomColumn(int start)
{ {
int nextColumn = Random.Next(start, TotalColumns); int nextColumn = Random.Next(start, TotalColumns);
while (PreviousPattern.ColumnHasObject(nextColumn)) RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
{
nextColumn = Random.Next(start, TotalColumns); nextColumn = Random.Next(start, TotalColumns);
});
return nextColumn; return nextColumn;
} }

View File

@ -234,7 +234,7 @@ private Pattern generateRandomNotes(int noteCount)
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
for (int i = 0; i < noteCount; i++) for (int i = 0; i < noteCount; i++)
{ {
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking) RunWhile(() => pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking, () =>
{ {
if (convertType.HasFlag(PatternType.Gathered)) if (convertType.HasFlag(PatternType.Gathered))
{ {
@ -244,7 +244,7 @@ private Pattern generateRandomNotes(int noteCount)
} }
else else
nextColumn = Random.Next(RandomStart, TotalColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
} });
addToPattern(pattern, nextColumn); addToPattern(pattern, nextColumn);
} }
@ -295,8 +295,10 @@ private Pattern generateRandomPatternWithMirrored(double centreProbability, doub
int nextColumn = Random.Next(RandomStart, columnLimit); int nextColumn = Random.Next(RandomStart, columnLimit);
for (int i = 0; i < noteCount; i++) for (int i = 0; i < noteCount; i++)
{ {
while (pattern.ColumnHasObject(nextColumn)) RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
{
nextColumn = Random.Next(RandomStart, columnLimit); nextColumn = Random.Next(RandomStart, columnLimit);
});
// Add normal note // Add normal note
addToPattern(pattern, nextColumn); addToPattern(pattern, nextColumn);

View File

@ -3,6 +3,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Logging;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
@ -12,6 +15,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
/// </summary> /// </summary>
internal abstract class PatternGenerator internal abstract class PatternGenerator
{ {
private const int max_rng_iterations = 100;
/// <summary> /// <summary>
/// The last pattern. /// The last pattern.
/// </summary> /// </summary>
@ -42,6 +47,21 @@ protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern pr
TotalColumns = Beatmap.TotalColumns; TotalColumns = Beatmap.TotalColumns;
} }
protected void RunWhile([InstantHandle] Func<bool> condition, Action action)
{
int iterations = 0;
while (condition() && iterations++ < max_rng_iterations)
action();
if (iterations < max_rng_iterations)
return;
// Generate + log an error/stacktrace
Logger.Log($"Allowable time exceeded for hitobject generation:\n{new StackTrace(0)}", level: LogLevel.Error);
}
/// <summary> /// <summary>
/// Generates the patterns for <see cref="HitObject"/>, each filled with hit objects. /// Generates the patterns for <see cref="HitObject"/>, each filled with hit objects.
/// </summary> /// </summary>