Skip to content

Commit

Permalink
Merge pull request #8 from NRG-Drink/feat/bench-and-adaptive-frames
Browse files Browse the repository at this point in the history
Feat/bench and adaptive frames
  • Loading branch information
NRG-Drink authored Jun 1, 2024
2 parents e972544 + 48854c4 commit e838b2c
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 56 deletions.
16 changes: 8 additions & 8 deletions NRG.Matrix/NRG.Matrix/DisplayObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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;
}

Expand All @@ -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)
Expand All @@ -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;
Expand Down
122 changes: 77 additions & 45 deletions NRG.Matrix/NRG.Matrix/Matrix.cs
Original file line number Diff line number Diff line change
@@ -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<DisplayObject> _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()
{
Expand All @@ -20,11 +33,20 @@ public void Enter()
Console.Clear();
while (true)
{
var sw = Stopwatch.StartNew();
AddObjects(Console.WindowWidth);
HandleObjects(Console.WindowHeight);
PrintObjects();

Task.Delay(_delay).Wait();
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
Expand All @@ -33,6 +55,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)
Expand Down Expand Up @@ -65,62 +101,74 @@ 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<ConsoleColor, DisplayObject>? traces)
private static void PrintToConsoleByLine(IGrouping<ConsoleColor, DisplayObject>? traces)
{
if (traces is null)
{
return;
}

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<DisplayObject> 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<IGrouping<ConsoleColor, DisplayObject>> others)
private static void PrintToConsoleByChar(IEnumerable<IGrouping<ConsoleColor, DisplayObject>> 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);
Expand All @@ -135,6 +183,7 @@ private void HandleWindowSizeChange()
{
return;
}

Console.Clear();
_displayObjects = _displayObjects
.Where(e => e.Pos.X < Console.WindowWidth)
Expand All @@ -143,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"
// );
//}
}
}
59 changes: 59 additions & 0 deletions NRG.Matrix/NRG.Matrix/Models/FactorsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
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;
}
}
4 changes: 4 additions & 0 deletions NRG.Matrix/NRG.Matrix/Models/Option.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +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; }
}
3 changes: 2 additions & 1 deletion NRG.Matrix/NRG.Matrix/NRG.Matrix.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
<PackageTags>cmd; matrix; code-rain; falling-code; matrix-digital-rain; digital-rain; matrix-rain; matrix-rain-characters;</PackageTags>
<PackAsTool>True</PackAsTool>
<ToolCommandName>matrix.enter</ToolCommandName>
<AssemblyVersion>1.0.0</AssemblyVersion>
<AssemblyVersion></AssemblyVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Version>1.1.0</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion NRG.Matrix/NRG.Matrix/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"profiles": {
"NRG.Matrix.App": {
"commandName": "Project",
"commandLineArgs": "-d 100 -a 1"
"commandLineArgs": "-d 80 -a 2 -m 20 -b true"
}
}
}
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ 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.
#### Max-Objects
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.

0 comments on commit e838b2c

Please sign in to comment.