Skip to content

Commit e838b2c

Browse files
authored
Merge pull request #8 from NRG-Drink/feat/bench-and-adaptive-frames
Feat/bench and adaptive frames
2 parents e972544 + 48854c4 commit e838b2c

File tree

7 files changed

+157
-56
lines changed

7 files changed

+157
-56
lines changed

NRG.Matrix/NRG.Matrix/DisplayObject.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ public record DisplayObject
66
{
77
private static readonly Random _random = new();
88
private readonly int _traceLength = 20 + _random.Next(0, 11);
9-
private string? _symbol;
10-
private string _lastRandom = GetRandomSymbol();
9+
private char? _symbol;
10+
private char _lastRandom = GetRandomSymbol();
1111

1212
public DisplayObject(int xRange)
1313
{
@@ -30,9 +30,9 @@ public DisplayObject(Point pos, int yOffset)
3030
public ConsoleColor Color { get; init; } = ConsoleColor.Gray;
3131
public bool IsTrace { get; init; } = false;
3232

33-
public string Symbol
33+
public char Symbol
3434
{
35-
get => _symbol is null ? GetNextRandomSymbol() : _symbol;
35+
get => _symbol is null ? GetNextRandomSymbol() : _symbol.Value;
3636
init => _symbol = value;
3737
}
3838

@@ -48,9 +48,9 @@ private DisplayObject GetTrace(int offset)
4848
=> new(Pos, offset);
4949

5050
private DisplayObject GetClear(int offset)
51-
=> new(Pos, offset) { Symbol = " " };
51+
=> new(Pos, offset) { Symbol = ' ' };
5252

53-
private string GetNextRandomSymbol()
53+
private char GetNextRandomSymbol()
5454
{
5555
var getRandom = ShouldGetNewRandom();
5656
if (!IsTrace && !getRandom)
@@ -65,8 +65,8 @@ private string GetNextRandomSymbol()
6565
return _lastRandom;
6666
}
6767

68-
private static string GetRandomSymbol()
69-
=> ((char)_random.Next(33, 123)).ToString();
68+
private static char GetRandomSymbol()
69+
=> (char)_random.Next(33, 123);
7070

7171
private static bool ShouldGetNewRandom()
7272
=> _random.Next(0, 3) == 0;

NRG.Matrix/NRG.Matrix/Matrix.cs

Lines changed: 77 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
11
using NRG.Matrix.App.Models;
2-
using System.Text;
2+
using NRG.Matrix.Models;
3+
using System.Diagnostics;
34

45
namespace NRG.Matrix.App;
56

67
public class Matrix(Option option)
78
{
9+
private readonly int _delay = Math.Clamp(option.Delay, 0, 9999);
10+
private readonly int _maxObjects = Math.Clamp(option.MaxObjects, 1, int.MaxValue);
11+
private readonly FactorsProvider _factorProvider = new()
12+
{
13+
Cadence = 20,
14+
Free = 3,
15+
Ease = 20,
16+
MaxAddRate = option.AddRate,
17+
};
18+
819
private List<DisplayObject> _displayObjects = [];
920
private (int Width, int Height) _windowDimension = (Console.WindowWidth, Console.WindowHeight);
10-
11-
private readonly float _addRate = option.AddRate;
12-
private readonly int _delay = option.Delay < 0 ? 0 : option.Delay;
13-
private readonly int _maxObjects = option.MaxObjects;
1421
private float _objectBuildup = 1;
22+
private float _addRate = 1;
23+
public float AddRate
24+
{
25+
get => _addRate;
26+
set => _addRate = Math.Clamp(value, 0.0001f, 999);
27+
}
1528

1629
public void Enter()
1730
{
@@ -20,11 +33,20 @@ public void Enter()
2033
Console.Clear();
2134
while (true)
2235
{
36+
var sw = Stopwatch.StartNew();
2337
AddObjects(Console.WindowWidth);
2438
HandleObjects(Console.WindowHeight);
2539
PrintObjects();
2640

27-
Task.Delay(_delay).Wait();
41+
if (option.IsBench == true)
42+
{
43+
PrintBenchValues(sw.ElapsedMilliseconds);
44+
}
45+
46+
var time = sw.ElapsedMilliseconds;
47+
var frameTimeOffset = option.MaxFrameTime - (int)time;
48+
AddRate = _factorProvider.AdjustAddRate(0, frameTimeOffset, _addRate);
49+
Task.Delay(Math.Max(0, _delay - (int)time)).Wait();
2850
}
2951
}
3052
finally
@@ -33,6 +55,20 @@ public void Enter()
3355
}
3456
}
3557

58+
private void PrintBenchValues(long time)
59+
{
60+
Console.SetCursorPosition(05, 01);
61+
Console.Write("╔═════════════════════════╗");
62+
Console.SetCursorPosition(05, 02);
63+
Console.Write($"║ Object add rate: {_addRate:00.00} ║");
64+
Console.SetCursorPosition(05, 03);
65+
Console.Write($"║ Frame calc. time: {time:00} ms ║");
66+
Console.SetCursorPosition(05, 04);
67+
Console.Write($"║ Max objects: {_displayObjects.Count,10} ║");
68+
Console.SetCursorPosition(05, 05);
69+
Console.Write("╚═════════════════════════╝");
70+
}
71+
3672
private void AddObjects(int width)
3773
{
3874
if (_maxObjects < _displayObjects.Count)
@@ -65,62 +101,74 @@ private void PrintObjects()
65101
{
66102
try
67103
{
104+
HandleWindowSizeChange();
68105
Console.CursorVisible = false;
69-
var validColors = _displayObjects.Where(e => e.Pos.Y >= 0).GroupBy(e => e.Color);
70-
71-
var traces = validColors.FirstOrDefault(e => e.Key is ConsoleColor.DarkGreen);
72-
PrintTrace(traces);
73-
74-
var others = validColors.Where(e => e.Key is not ConsoleColor.DarkGreen);
75-
PrintOthers(others);
106+
var validColorGroups = _displayObjects
107+
.Where(e => e.Pos.Y >= 0)
108+
.GroupBy(e => e.Color);
109+
110+
var traces = validColorGroups.FirstOrDefault(e => e.Key is ConsoleColor.DarkGreen);
111+
PrintToConsoleByLine(traces);
112+
var others = validColorGroups.Where(e => e.Key is not ConsoleColor.DarkGreen);
113+
PrintToConsoleByChar(others);
76114
}
77-
catch (ArgumentOutOfRangeException)
115+
catch (ArgumentOutOfRangeException ex)
78116
{
79117
// Window was resized to a smaller size.
80118
HandleWindowSizeChange();
81119
}
82120
}
83121

84-
private void PrintTrace(IGrouping<ConsoleColor, DisplayObject>? traces)
122+
private static void PrintToConsoleByLine(IGrouping<ConsoleColor, DisplayObject>? traces)
85123
{
86124
if (traces is null)
87125
{
88126
return;
89127
}
90128

91129
var orderedRows = traces.OrderBy(e => e.Pos.X).GroupBy(e => e.Pos.Y);
92-
Console.ForegroundColor = ConsoleColor.DarkGreen;
130+
Console.ForegroundColor = traces.Key;
93131
foreach (var row in orderedRows)
94132
{
95-
var obj = row.ToList();
96-
var first = obj.First().Pos.X;
97-
var last = obj.Last().Pos.X;
133+
var first = row.First().Pos.X;
134+
var last = row.Last().Pos.X;
135+
136+
var line = GetLineText([.. row], first, last);
137+
var width = Console.WindowWidth;
138+
while (last > width && first > width)
139+
{
140+
line = line[..(last - width)];
141+
width = Console.WindowWidth;
142+
}
143+
if (first > Console.WindowWidth || last > Console.WindowWidth)
144+
{
145+
continue;
146+
}
98147

99148
Console.SetCursorPosition(first, row.Key);
100-
var line = GetLineText(obj, first, last);
101149
Console.Write(line);
102150
}
103151
}
104152

105153
private static string GetLineText(List<DisplayObject> obj, int first, int last)
106154
{
107-
var len = last - first - obj.Count + 1;
108-
var line = string.Join("", Enumerable.Repeat(" ", len));
109-
var sb = new StringBuilder(line);
155+
var line = new char[last - first + 1];
156+
Array.Fill(line, ' ');
157+
110158
foreach (var o in obj)
111159
{
112-
sb.Insert(o.Pos.X - first, o.Symbol);
160+
line[o.Pos.X - first] = o.Symbol;
113161
}
114162

115-
return sb.ToString();
163+
return new string(line);
116164
}
117165

118-
private void PrintOthers(IEnumerable<IGrouping<ConsoleColor, DisplayObject>> others)
166+
private static void PrintToConsoleByChar(IEnumerable<IGrouping<ConsoleColor, DisplayObject>> others)
119167
{
120168
foreach (var group in others)
121169
{
122170
Console.ForegroundColor = group.Key;
123-
foreach (var obj in group)
171+
foreach (var obj in group.Where(e => e.Pos.X < Console.WindowWidth))
124172
{
125173
Console.SetCursorPosition(obj.Pos.X, obj.Pos.Y);
126174
Console.Write(obj.Symbol);
@@ -135,6 +183,7 @@ private void HandleWindowSizeChange()
135183
{
136184
return;
137185
}
186+
138187
Console.Clear();
139188
_displayObjects = _displayObjects
140189
.Where(e => e.Pos.X < Console.WindowWidth)
@@ -143,27 +192,10 @@ private void HandleWindowSizeChange()
143192
_windowDimension = (Console.WindowWidth, Console.WindowHeight);
144193
}
145194

146-
//private void HandleBenchmarkMode()
147-
//{
148-
// Console.SetCursorPosition(5, 1);
149-
// Console.Write($"Number of objects in RAM: {_displayObjects.Count} ");
150-
// _frames++;
151-
//}
152-
153-
private void LeaveMatrix(int delay, TimeSpan? time = null)
195+
private static void LeaveMatrix(int delay, TimeSpan? time = null)
154196
{
155197
Console.CursorVisible = true;
156198
Console.ForegroundColor = ConsoleColor.Gray;
157199
Console.SetCursorPosition(0, Console.WindowHeight + 1);
158-
//if (!_isEndlessMode)
159-
//{
160-
// Console.WriteLine(
161-
// $"Rendered Frames: {_frames} " +
162-
// $"of possible {time!.Value.TotalMilliseconds / delay} " +
163-
// $"in time {time} (hh:MM:ss)\n" +
164-
// $"With an average calculation time of " +
165-
// $"{((time!.Value.TotalMilliseconds - delay * _frames) / _frames):0.0} ms per frame"
166-
// );
167-
//}
168200
}
169201
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
namespace NRG.Matrix.Models;
2+
3+
public class FactorsProvider
4+
{
5+
private readonly float[] _factors;
6+
private int counter = -10;
7+
8+
public FactorsProvider()
9+
{
10+
_factors = InitFactors();
11+
}
12+
13+
public int Cadence { get; init; } = 20;
14+
public int Free { get; init; } = 3;
15+
public int Ease { get; init; } = 20;
16+
public float MaxAddRate { get; init; } = 5;
17+
18+
public float AdjustAddRate(int target, int value, float addRate)
19+
{
20+
if (++counter < Cadence)
21+
{
22+
return addRate;
23+
}
24+
counter = 0;
25+
26+
var offset = target - value;
27+
var factor = GetFactor(offset);
28+
29+
return Math.Clamp(addRate * factor, 0.00001f, MaxAddRate);
30+
}
31+
32+
private float GetFactor(int offset)
33+
{
34+
var abs = Math.Abs(offset);
35+
var clamp = Math.Clamp(abs, 0, _factors.Length - 1);
36+
37+
var factor = _factors[clamp];
38+
39+
if (offset < 0)
40+
{
41+
return 1f + factor;
42+
}
43+
44+
return 1f - factor;
45+
}
46+
47+
private float[] InitFactors()
48+
{
49+
var factors = new float[Free + Ease];
50+
Array.Fill(factors, 0);
51+
52+
for (int i = 0; i < Ease; i++)
53+
{
54+
factors[Free + i] = (float)i / (float)(Ease * 1.2f);
55+
}
56+
57+
return factors;
58+
}
59+
}

NRG.Matrix/NRG.Matrix/Models/Option.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ public class Option
1010
public int MaxObjects { get; init; }
1111
[Option('a', "add-rate", Required = false, Default = 1, HelpText = "Number of objects added each frame (float):")]
1212
public float AddRate { get; init; }
13+
[Option('m', "max-frame-time", Required = false, Default = 20, HelpText = "Maximum calculation time of a frame (prevent lagging).")]
14+
public int MaxFrameTime { get; init; }
15+
[Option('b', "bench-mode", Required = false, Default = false, HelpText = "Enter benchmark-mode to see computing stats.")]
16+
public bool? IsBench { get; init; }
1317
}

NRG.Matrix/NRG.Matrix/NRG.Matrix.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
<PackageTags>cmd; matrix; code-rain; falling-code; matrix-digital-rain; digital-rain; matrix-rain; matrix-rain-characters;</PackageTags>
1515
<PackAsTool>True</PackAsTool>
1616
<ToolCommandName>matrix.enter</ToolCommandName>
17-
<AssemblyVersion>1.0.0</AssemblyVersion>
17+
<AssemblyVersion></AssemblyVersion>
1818
<PackageLicenseExpression>MIT</PackageLicenseExpression>
19+
<Version>1.1.0</Version>
1920
</PropertyGroup>
2021

2122
<ItemGroup>

NRG.Matrix/NRG.Matrix/Properties/launchSettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"profiles": {
33
"NRG.Matrix.App": {
44
"commandName": "Project",
5-
"commandLineArgs": "-d 100 -a 1"
5+
"commandLineArgs": "-d 80 -a 2 -m 20 -b true"
66
}
77
}
88
}

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,16 @@ matrix.enter
1515

1616
## Start With Parameters
1717
```cmd
18-
matrix.enter --delay 80 --add-rate 1 --max-objects 9999
18+
matrix.enter --delay 80 --add-rate 1 --max-objects 9999 --max-frame-time 20 --bench-mode false
1919
```
2020
#### Delay
2121
This will set a pause in milliseconds (ms) between the frames.
2222
#### Max-Objects
2323
Will set the maximum number of objects. When the maximum number is reached, no more objects will be created. (object = falling drop)
2424
#### Add-Rate
2525
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.
26+
#### Max-Frame-Time
27+
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.
28+
#### Bench-Mode
29+
Thiss will toggle the benchmark-mode to see the frame calculation time and the current add-rate.
30+

0 commit comments

Comments
 (0)