From 756cb1f9c8d8165b550f0bc462ee4a9b921dc902 Mon Sep 17 00:00:00 2001 From: NRG-Drink Date: Sat, 1 Jun 2024 21:42:25 +0200 Subject: [PATCH 1/3] chore: implement new bench mode #6 --- NRG.Matrix/NRG.Matrix/Matrix.cs | 19 ++++++++++++++++++- NRG.Matrix/NRG.Matrix/Models/Option.cs | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/NRG.Matrix/NRG.Matrix/Matrix.cs b/NRG.Matrix/NRG.Matrix/Matrix.cs index 8781338..7ea9961 100644 --- a/NRG.Matrix/NRG.Matrix/Matrix.cs +++ b/NRG.Matrix/NRG.Matrix/Matrix.cs @@ -24,7 +24,10 @@ public void Enter() HandleObjects(Console.WindowHeight); PrintObjects(); - Task.Delay(_delay).Wait(); + if (option.IsBench == true) + { + PrintBenchValues(sw.ElapsedMilliseconds); + } } } finally @@ -33,6 +36,20 @@ public void Enter() } } + private void PrintBenchValues(long time) + { + Console.SetCursorPosition(05, 01); + Console.Write("╔═════════════════════════╗"); + Console.SetCursorPosition(05, 02); + Console.Write($"║ Object add rate: {_addRate:00.00} ║"); + Console.SetCursorPosition(05, 03); + Console.Write($"║ Frame calc. time: {time:00} ms ║"); + Console.SetCursorPosition(05, 04); + Console.Write($"║ Max objects: {_displayObjects.Count,10} ║"); + Console.SetCursorPosition(05, 05); + Console.Write("╚═════════════════════════╝"); + } + private void AddObjects(int width) { if (_maxObjects < _displayObjects.Count) diff --git a/NRG.Matrix/NRG.Matrix/Models/Option.cs b/NRG.Matrix/NRG.Matrix/Models/Option.cs index 0ad41d0..b94f8f9 100644 --- a/NRG.Matrix/NRG.Matrix/Models/Option.cs +++ b/NRG.Matrix/NRG.Matrix/Models/Option.cs @@ -10,4 +10,6 @@ public class Option public int MaxObjects { get; init; } [Option('a', "add-rate", Required = false, Default = 1, HelpText = "Number of objects added each frame (float):")] public float AddRate { get; init; } + [Option('b', "bench-mode", Required = false, Default = false, HelpText = "Enter benchmark-mode to see computing stats.")] + public bool? IsBench { get; init; } } From af825acfbf05f5edd0b4789a6e773c4fa24a7349 Mon Sep 17 00:00:00 2001 From: NRG-Drink Date: Sat, 1 Jun 2024 21:42:54 +0200 Subject: [PATCH 2/3] chore: implement adaptive frame rates and little performance improvements --- NRG.Matrix/NRG.Matrix/DisplayObject.cs | 16 +-- NRG.Matrix/NRG.Matrix/Matrix.cs | 105 ++++++++++-------- .../NRG.Matrix/Models/FactorsProvider.cs | 61 ++++++++++ NRG.Matrix/NRG.Matrix/Models/Option.cs | 2 + NRG.Matrix/NRG.Matrix/NRG.Matrix.csproj | 3 +- .../NRG.Matrix/Properties/launchSettings.json | 2 +- README.md | 7 +- 7 files changed, 140 insertions(+), 56 deletions(-) create mode 100644 NRG.Matrix/NRG.Matrix/Models/FactorsProvider.cs diff --git a/NRG.Matrix/NRG.Matrix/DisplayObject.cs b/NRG.Matrix/NRG.Matrix/DisplayObject.cs index 494f871..c001b1c 100644 --- a/NRG.Matrix/NRG.Matrix/DisplayObject.cs +++ b/NRG.Matrix/NRG.Matrix/DisplayObject.cs @@ -6,8 +6,8 @@ public record DisplayObject { private static readonly Random _random = new(); private readonly int _traceLength = 20 + _random.Next(0, 11); - private string? _symbol; - private string _lastRandom = GetRandomSymbol(); + private char? _symbol; + private char _lastRandom = GetRandomSymbol(); public DisplayObject(int xRange) { @@ -30,9 +30,9 @@ public DisplayObject(Point pos, int yOffset) public ConsoleColor Color { get; init; } = ConsoleColor.Gray; public bool IsTrace { get; init; } = false; - public string Symbol + public char Symbol { - get => _symbol is null ? GetNextRandomSymbol() : _symbol; + get => _symbol is null ? GetNextRandomSymbol() : _symbol.Value; init => _symbol = value; } @@ -48,9 +48,9 @@ private DisplayObject GetTrace(int offset) => new(Pos, offset); private DisplayObject GetClear(int offset) - => new(Pos, offset) { Symbol = " " }; + => new(Pos, offset) { Symbol = ' ' }; - private string GetNextRandomSymbol() + private char GetNextRandomSymbol() { var getRandom = ShouldGetNewRandom(); if (!IsTrace && !getRandom) @@ -65,8 +65,8 @@ private string GetNextRandomSymbol() return _lastRandom; } - private static string GetRandomSymbol() - => ((char)_random.Next(33, 123)).ToString(); + private static char GetRandomSymbol() + => (char)_random.Next(33, 123); private static bool ShouldGetNewRandom() => _random.Next(0, 3) == 0; diff --git a/NRG.Matrix/NRG.Matrix/Matrix.cs b/NRG.Matrix/NRG.Matrix/Matrix.cs index 7ea9961..9d6ca3b 100644 --- a/NRG.Matrix/NRG.Matrix/Matrix.cs +++ b/NRG.Matrix/NRG.Matrix/Matrix.cs @@ -1,17 +1,30 @@ using NRG.Matrix.App.Models; -using System.Text; +using NRG.Matrix.Models; +using System.Diagnostics; namespace NRG.Matrix.App; public class Matrix(Option option) { + private readonly int _delay = Math.Clamp(option.Delay, 0, 9999); + private readonly int _maxObjects = Math.Clamp(option.MaxObjects, 1, int.MaxValue); + private readonly FactorsProvider _factorProvider = new() + { + Cadence = 20, + Free = 3, + Ease = 20, + MaxAddRate = option.AddRate, + }; + private List _displayObjects = []; private (int Width, int Height) _windowDimension = (Console.WindowWidth, Console.WindowHeight); - - private readonly float _addRate = option.AddRate; - private readonly int _delay = option.Delay < 0 ? 0 : option.Delay; - private readonly int _maxObjects = option.MaxObjects; private float _objectBuildup = 1; + private float _addRate = 1; + public float AddRate + { + get => _addRate; + set => _addRate = Math.Clamp(value, 0.0001f, 999); + } public void Enter() { @@ -20,6 +33,7 @@ public void Enter() Console.Clear(); while (true) { + var sw = Stopwatch.StartNew(); AddObjects(Console.WindowWidth); HandleObjects(Console.WindowHeight); PrintObjects(); @@ -27,7 +41,12 @@ public void Enter() if (option.IsBench == true) { PrintBenchValues(sw.ElapsedMilliseconds); - } + } + + var time = sw.ElapsedMilliseconds; + var frameTimeOffset = option.MaxFrameTime - (int)time; + AddRate = _factorProvider.AdjustAddRate(0, frameTimeOffset, _addRate); + Task.Delay(Math.Max(0, _delay - (int)time)).Wait(); } } finally @@ -82,23 +101,25 @@ private void PrintObjects() { try { + HandleWindowSizeChange(); Console.CursorVisible = false; - var validColors = _displayObjects.Where(e => e.Pos.Y >= 0).GroupBy(e => e.Color); - - var traces = validColors.FirstOrDefault(e => e.Key is ConsoleColor.DarkGreen); - PrintTrace(traces); - - var others = validColors.Where(e => e.Key is not ConsoleColor.DarkGreen); - PrintOthers(others); + var validColorGroups = _displayObjects + .Where(e => e.Pos.Y >= 0) + .GroupBy(e => e.Color); + + var traces = validColorGroups.FirstOrDefault(e => e.Key is ConsoleColor.DarkGreen); + PrintToConsoleByLine(traces); + var others = validColorGroups.Where(e => e.Key is not ConsoleColor.DarkGreen); + PrintToConsoleByChar(others); } - catch (ArgumentOutOfRangeException) + catch (ArgumentOutOfRangeException ex) { // Window was resized to a smaller size. HandleWindowSizeChange(); } } - private void PrintTrace(IGrouping? traces) + private static void PrintToConsoleByLine(IGrouping? traces) { if (traces is null) { @@ -106,38 +127,48 @@ private void PrintTrace(IGrouping? traces) } var orderedRows = traces.OrderBy(e => e.Pos.X).GroupBy(e => e.Pos.Y); - Console.ForegroundColor = ConsoleColor.DarkGreen; + Console.ForegroundColor = traces.Key; foreach (var row in orderedRows) { - var obj = row.ToList(); - var first = obj.First().Pos.X; - var last = obj.Last().Pos.X; + var first = row.First().Pos.X; + var last = row.Last().Pos.X; + + var line = GetLineText([.. row], first, last); + var width = Console.WindowWidth; + while (last > width && first > width) + { + line = line[.. (last - width)]; + width = Console.WindowWidth; + } + if (first > Console.WindowWidth || last > Console.WindowWidth) + { + continue; + } Console.SetCursorPosition(first, row.Key); - var line = GetLineText(obj, first, last); Console.Write(line); } } private static string GetLineText(List obj, int first, int last) { - var len = last - first - obj.Count + 1; - var line = string.Join("", Enumerable.Repeat(" ", len)); - var sb = new StringBuilder(line); + var line = new char[last - first + 1]; + Array.Fill(line, ' '); + foreach (var o in obj) { - sb.Insert(o.Pos.X - first, o.Symbol); + line[o.Pos.X - first] = o.Symbol; } - return sb.ToString(); + return new string(line); } - private void PrintOthers(IEnumerable> others) + private static void PrintToConsoleByChar(IEnumerable> others) { foreach (var group in others) { Console.ForegroundColor = group.Key; - foreach (var obj in group) + foreach (var obj in group.Where(e => e.Pos.X < Console.WindowWidth)) { Console.SetCursorPosition(obj.Pos.X, obj.Pos.Y); Console.Write(obj.Symbol); @@ -152,6 +183,7 @@ private void HandleWindowSizeChange() { return; } + Console.Clear(); _displayObjects = _displayObjects .Where(e => e.Pos.X < Console.WindowWidth) @@ -160,27 +192,10 @@ private void HandleWindowSizeChange() _windowDimension = (Console.WindowWidth, Console.WindowHeight); } - //private void HandleBenchmarkMode() - //{ - // Console.SetCursorPosition(5, 1); - // Console.Write($"Number of objects in RAM: {_displayObjects.Count} "); - // _frames++; - //} - - private void LeaveMatrix(int delay, TimeSpan? time = null) + private static void LeaveMatrix(int delay, TimeSpan? time = null) { Console.CursorVisible = true; Console.ForegroundColor = ConsoleColor.Gray; Console.SetCursorPosition(0, Console.WindowHeight + 1); - //if (!_isEndlessMode) - //{ - // Console.WriteLine( - // $"Rendered Frames: {_frames} " + - // $"of possible {time!.Value.TotalMilliseconds / delay} " + - // $"in time {time} (hh:MM:ss)\n" + - // $"With an average calculation time of " + - // $"{((time!.Value.TotalMilliseconds - delay * _frames) / _frames):0.0} ms per frame" - // ); - //} } } diff --git a/NRG.Matrix/NRG.Matrix/Models/FactorsProvider.cs b/NRG.Matrix/NRG.Matrix/Models/FactorsProvider.cs new file mode 100644 index 0000000..d64fcd3 --- /dev/null +++ b/NRG.Matrix/NRG.Matrix/Models/FactorsProvider.cs @@ -0,0 +1,61 @@ +using NRG.Matrix.App.Models; + +namespace NRG.Matrix.Models; + +public class FactorsProvider +{ + private readonly float[] _factors; + private int counter = -10; + + public FactorsProvider() + { + _factors = InitFactors(); + } + + public int Cadence { get; init; } = 20; + public int Free { get; init; } = 3; + public int Ease { get; init; } = 20; + public float MaxAddRate { get; init; } = 5; + + public float AdjustAddRate(int target, int value, float addRate) + { + if (++counter < Cadence) + { + return addRate; + } + counter = 0; + + var offset = target - value; + var factor = GetFactor(offset); + + return Math.Clamp(addRate * factor, 0.00001f, MaxAddRate); + } + + private float GetFactor(int offset) + { + var abs = Math.Abs(offset); + var clamp = Math.Clamp(abs, 0, _factors.Length - 1); + + var factor = _factors[clamp]; + + if (offset < 0) + { + return 1f + factor; + } + + return 1f - factor; + } + + private float[] InitFactors() + { + var factors = new float[Free + Ease]; + Array.Fill(factors, 0); + + for (int i = 0; i < Ease; i++) + { + factors[Free + i] = (float)i / (float)(Ease * 1.2f); + } + + return factors; + } +} diff --git a/NRG.Matrix/NRG.Matrix/Models/Option.cs b/NRG.Matrix/NRG.Matrix/Models/Option.cs index b94f8f9..7fa72ad 100644 --- a/NRG.Matrix/NRG.Matrix/Models/Option.cs +++ b/NRG.Matrix/NRG.Matrix/Models/Option.cs @@ -10,6 +10,8 @@ public class Option public int MaxObjects { get; init; } [Option('a', "add-rate", Required = false, Default = 1, HelpText = "Number of objects added each frame (float):")] public float AddRate { get; init; } + [Option('m', "max-frame-time", Required = false, Default = 20, HelpText = "Maximum calculation time of a frame (prevent lagging).")] + public int MaxFrameTime { get; init; } [Option('b', "bench-mode", Required = false, Default = false, HelpText = "Enter benchmark-mode to see computing stats.")] public bool? IsBench { get; init; } } diff --git a/NRG.Matrix/NRG.Matrix/NRG.Matrix.csproj b/NRG.Matrix/NRG.Matrix/NRG.Matrix.csproj index 1c64c5d..58c55da 100644 --- a/NRG.Matrix/NRG.Matrix/NRG.Matrix.csproj +++ b/NRG.Matrix/NRG.Matrix/NRG.Matrix.csproj @@ -14,8 +14,9 @@ cmd; matrix; code-rain; falling-code; matrix-digital-rain; digital-rain; matrix-rain; matrix-rain-characters; True matrix.enter - 1.0.0 + MIT + 1.1.0 diff --git a/NRG.Matrix/NRG.Matrix/Properties/launchSettings.json b/NRG.Matrix/NRG.Matrix/Properties/launchSettings.json index 45b5162..6706cc0 100644 --- a/NRG.Matrix/NRG.Matrix/Properties/launchSettings.json +++ b/NRG.Matrix/NRG.Matrix/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "NRG.Matrix.App": { "commandName": "Project", - "commandLineArgs": "-d 100 -a 1" + "commandLineArgs": "-d 80 -a 2 -m 20 -b true" } } } \ No newline at end of file diff --git a/README.md b/README.md index a0405b2..f8211b6 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ matrix.enter ## Start With Parameters ```cmd -matrix.enter --delay 80 --add-rate 1 --max-objects 9999 +matrix.enter --delay 80 --add-rate 1 --max-objects 9999 --max-frame-time 20 --bench-mode false ``` #### Delay This will set a pause in milliseconds (ms) between the frames. @@ -23,3 +23,8 @@ This will set a pause in milliseconds (ms) between the frames. Will set the maximum number of objects. When the maximum number is reached, no more objects will be created. (object = falling drop) #### Add-Rate A factor to a function who depends on screen width. It has impact on the number of objects that are added to the screen on each frame. +#### Max-Frame-Time +This will set a target time for a frame to display, if the frame takes more time to calculate, the add-rate will be decreased until it can handle it. +#### Bench-Mode +Thiss will toggle the benchmark-mode to see the frame calculation time and the current add-rate. + From 48854c4caa09ab6c807cebd4ae282634d2f308a6 Mon Sep 17 00:00:00 2001 From: NRG-Drink Date: Sat, 1 Jun 2024 21:44:11 +0200 Subject: [PATCH 3/3] style: project cleanup --- NRG.Matrix/NRG.Matrix/Matrix.cs | 2 +- NRG.Matrix/NRG.Matrix/Models/FactorsProvider.cs | 6 ++---- NRG.Matrix/NRG.Matrix/Models/Option.cs | 8 ++++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/NRG.Matrix/NRG.Matrix/Matrix.cs b/NRG.Matrix/NRG.Matrix/Matrix.cs index 9d6ca3b..7050f44 100644 --- a/NRG.Matrix/NRG.Matrix/Matrix.cs +++ b/NRG.Matrix/NRG.Matrix/Matrix.cs @@ -137,7 +137,7 @@ private static void PrintToConsoleByLine(IGrouping? var width = Console.WindowWidth; while (last > width && first > width) { - line = line[.. (last - width)]; + line = line[..(last - width)]; width = Console.WindowWidth; } if (first > Console.WindowWidth || last > Console.WindowWidth) diff --git a/NRG.Matrix/NRG.Matrix/Models/FactorsProvider.cs b/NRG.Matrix/NRG.Matrix/Models/FactorsProvider.cs index d64fcd3..171c7f5 100644 --- a/NRG.Matrix/NRG.Matrix/Models/FactorsProvider.cs +++ b/NRG.Matrix/NRG.Matrix/Models/FactorsProvider.cs @@ -1,6 +1,4 @@ -using NRG.Matrix.App.Models; - -namespace NRG.Matrix.Models; +namespace NRG.Matrix.Models; public class FactorsProvider { @@ -17,7 +15,7 @@ public FactorsProvider() public int Ease { get; init; } = 20; public float MaxAddRate { get; init; } = 5; - public float AdjustAddRate(int target, int value, float addRate) + public float AdjustAddRate(int target, int value, float addRate) { if (++counter < Cadence) { diff --git a/NRG.Matrix/NRG.Matrix/Models/Option.cs b/NRG.Matrix/NRG.Matrix/Models/Option.cs index 7fa72ad..86e35c9 100644 --- a/NRG.Matrix/NRG.Matrix/Models/Option.cs +++ b/NRG.Matrix/NRG.Matrix/Models/Option.cs @@ -10,8 +10,8 @@ public class Option public int MaxObjects { get; init; } [Option('a', "add-rate", Required = false, Default = 1, HelpText = "Number of objects added each frame (float):")] public float AddRate { get; init; } - [Option('m', "max-frame-time", Required = false, Default = 20, HelpText = "Maximum calculation time of a frame (prevent lagging).")] - public int MaxFrameTime { get; init; } - [Option('b', "bench-mode", Required = false, Default = false, HelpText = "Enter benchmark-mode to see computing stats.")] - public bool? IsBench { get; init; } + [Option('m', "max-frame-time", Required = false, Default = 20, HelpText = "Maximum calculation time of a frame (prevent lagging).")] + public int MaxFrameTime { get; init; } + [Option('b', "bench-mode", Required = false, Default = false, HelpText = "Enter benchmark-mode to see computing stats.")] + public bool? IsBench { get; init; } }