diff --git a/13_Bounce/csharp/Game.cs b/13_Bounce/csharp/Game.cs new file mode 100644 index 00000000..1bf8236d --- /dev/null +++ b/13_Bounce/csharp/Game.cs @@ -0,0 +1,149 @@ +using System.Text; +using static Bounce.Resources.Resource; + +namespace Bounce; + +internal class Game +{ + private const int _chartWidth = 70; + private const float _acceleration = -32; // feet/s^2 + + private readonly IReadWrite _io; + + public Game(IReadWrite io) + { + _io = io; + } + + public void Play() + { + _io.Write(Streams.Title); + _io.Write(Streams.Instructions); + + var increment = _io.ReadParameter("Time increment (sec)"); + var velocity = _io.ReadParameter("Velocity (fps)"); + var elasticity = _io.ReadParameter("Coefficient"); + + var timeToFirstBounce = -2 * velocity / _acceleration; + var bounceCount = (int)(Graph.Row.Width * increment / timeToFirstBounce); + + var maxHeight = (float)Math.Round(-velocity * velocity / 2 / _acceleration, MidpointRounding.AwayFromZero); + + var graph = new Graph(maxHeight, increment); + + var totalTime = 0f; + for (var bounce = 0; bounce < bounceCount; bounce++, velocity *= elasticity) + { + var bounceDuration = -2 * velocity / _acceleration; + for (var time = 0f; time <= bounceDuration; totalTime += increment, time += increment) + { + var height = velocity * time + _acceleration * time * time / 2; + graph.Plot(totalTime, height); + } + } + + _io.WriteLine(graph); + } +} + +internal class Graph +{ + private readonly Dictionary _rows; + private readonly float _increment; + private float _maxTimePlotted; + + public Graph(float maxHeight, float increment) + { + // 1 row == 1/2 foot + 1 row for zero + var rowCount = 2 * (int)Math.Round(maxHeight, MidpointRounding.AwayFromZero) + 1; + _rows = Enumerable.Range(0, rowCount) + .ToDictionary(x => x, x => new Row(x % 2 == 0 ? $" {x / 2} " : "")); + _increment = increment; + } + + public void Plot(float time, float height) + { + var rowIndex = (int)Math.Round(height * 2, MidpointRounding.AwayFromZero); + var colIndex = (int)(time / _increment) + 1; + if (_rows.TryGetValue(rowIndex, out var row)) + { + row[colIndex] = '0'; + } + _maxTimePlotted = Math.Max(time, _maxTimePlotted); + } + + public override string ToString() + { + var sb = new StringBuilder(); + foreach (var (_, row) in _rows.OrderByDescending(x => x.Key)) + { + sb.Append(row).AppendLine(); + } + var maxTimeMark = (int)Math.Ceiling(_maxTimePlotted); + sb.Append(' ').Append('.', (int)(maxTimeMark/_increment) + 1).AppendLine(); + + var timeLabels = new Labels(); + for (var i = 1; i <= maxTimeMark; i++) + { + timeLabels.Add((int)(i / _increment), $" {i} "); + } + sb.Append(timeLabels).AppendLine(); + sb.Append(' ', (int)((int)(_maxTimePlotted + 1)/_increment/2 - 2)).AppendLine("Seconds"); + + return sb.ToString(); + } + + internal class Row + { + public const int Width = 70; + + private readonly char[] _chars = new char[Width + 2]; + private int nextColumn = 0; + + public Row(string label) + { + Array.Fill(_chars, ' '); + Array.Copy(label.ToCharArray(), _chars, label.Length); + nextColumn = label.Length; + } + + public char this[int column] + { + set + { + if (column >= _chars.Length) { return; } + if (column < nextColumn) { column = nextColumn; } + _chars[column] = value; + nextColumn = column + 1; + } + } + + public override string ToString() => new string(_chars); + } + + internal class Labels : Row + { + public Labels() + : base(" 0") + { + } + + public void Add(int column, string label) + { + for (var i = 0; i < label.Length; i++) + { + this[column + i] = label[i]; + } + } + } +} + +internal static class IReadWriteExtensions +{ + internal static float ReadParameter(this IReadWrite io, string parameter) + { + var value = io.ReadNumber(parameter); + io.WriteLine(); + return value; + } +} \ No newline at end of file diff --git a/13_Bounce/csharp/Program.cs b/13_Bounce/csharp/Program.cs new file mode 100644 index 00000000..3b79a341 --- /dev/null +++ b/13_Bounce/csharp/Program.cs @@ -0,0 +1,6 @@ +global using Games.Common.IO; +global using Games.Common.Numbers; + +using Bounce; + +new Game(new ConsoleIO()).Play(); \ No newline at end of file