From 337e7976d13b1aea8ac8282c6bf440c2abbf202a Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 09:50:35 -0300 Subject: [PATCH 01/14] Performance optimization, reducing the amount of writes to the console necessary to output each iteration's matrix. --- 55_Life/csharp/Program.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/55_Life/csharp/Program.cs b/55_Life/csharp/Program.cs index 47ed7386..17e6fb18 100644 --- a/55_Life/csharp/Program.cs +++ b/55_Life/csharp/Program.cs @@ -132,12 +132,14 @@ void ProcessSimulation() var nextMinX = maxHeight - 1; var nextMinY = maxWidth - 1; var nextMaxX = 0; - var nextMaxY = 0; + var nextMaxY = 0; + + var matrixOutput = new StringBuilder(); // prints the empty lines before search area for (var x = 0; x < minX; x++) { - Console.WriteLine(); + matrixOutput.AppendLine(); } // refreshes the matrix and updates search area @@ -168,14 +170,15 @@ void ProcessSimulation() nextMaxY = Math.Max(y + 1, nextMaxY); } - Console.WriteLine(string.Join(separator: null, values: printedLine)); + matrixOutput.AppendLine(string.Join(separator: null, values: printedLine)); } // prints empty lines after search area for (var x = maxX + 1; x < maxHeight; x++) { - Console.WriteLine(); + matrixOutput.AppendLine(); } + Console.WriteLine(matrixOutput); Console.WriteLine(); void UpdateSearchArea() From f3d63355df68b53aaa31808b71cd690371235eaa Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 10:09:31 -0300 Subject: [PATCH 02/14] Adding instructions on how to run the example. --- 55_Life/csharp/Program.cs | 18 +----------------- 55_Life/csharp/README.md | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/55_Life/csharp/Program.cs b/55_Life/csharp/Program.cs index 17e6fb18..1b70ed3a 100644 --- a/55_Life/csharp/Program.cs +++ b/55_Life/csharp/Program.cs @@ -1,20 +1,4 @@ -/* - * LIFE - * An implementation of John Conway's popular cellular automaton - * Ported by Dyego Alekssander Maas - * - * An example pattern would be: - * " * " - * "***" - * "DONE" (indicates that the simulation can start) - * - * You can find patterns to play with here: http://pi.math.cornell.edu/~lipa/mec/lesson6.html - * - * Optionally, you can run this program with the "--wait 1000" argument, the number being the time in milliseconds - * that the application will pause between each iteration. This is enables you to watch the simulation unfolding. - * By default, there is no pause between iterations. -*/ -using System.Text; +using System.Text; const int maxWidth = 70; const int maxHeight = 24; diff --git a/55_Life/csharp/README.md b/55_Life/csharp/README.md index 4daabb5c..3bf2d48a 100644 --- a/55_Life/csharp/README.md +++ b/55_Life/csharp/README.md @@ -1,3 +1,26 @@ +# Life + +An implementation of John Conway's popular cellular automaton, also know as **Conway's Game of Life**. The original source was downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html). + +Ported by Dyego Alekssander Maas. + +## How to run + +This program requires you to install [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0). After installed, you just need to run `dotnet run` from this directory in the terminal. + +## Know more about Conway's Game of Life + +You can find more about Conway's Game of Life on this page of the [Cornell Math Explorers' Club](http://pi.math.cornell.edu/~lipa/mec/lesson6.html), alongside many examples of patterns you can try. + +### Optional parameters + +Optionally, you can run this program with the `--wait 1000` argument, the number being the time in milliseconds +that the application will pause between each iteration. This is enables you to watch the simulation unfolding. By default, there is no pause between iterations. + +The complete command would be `dotnet run --wait 1000`. + +## Instructions to the port + Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) From f62111606403a5914cedcdfa07b03a984ed11073 Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 10:50:36 -0300 Subject: [PATCH 03/14] Adding instructions on how to enter patterns, and also some examples. --- 55_Life/csharp/README.md | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/55_Life/csharp/README.md b/55_Life/csharp/README.md index 3bf2d48a..5b4ffe62 100644 --- a/55_Life/csharp/README.md +++ b/55_Life/csharp/README.md @@ -19,6 +19,51 @@ that the application will pause between each iteration. This is enables you to w The complete command would be `dotnet run --wait 1000`. +## Entering patterns + +Once running the game, you are expected to enter a pattern. This pattern consists of multiple lines of text with either **spaces** or **some character**, usually an asterisk (`*`). + +Spaces represent empty cells. Asterisks represent alive cells. + +After entering the pattern, you need to enter the word "DONE". It is not case sensitive. An example of pattern would be: + +``` + * +*** +DONE +``` + +### Some patterns you could try + +``` + * +*** +``` + +``` +* +*** +``` + +``` +** +** +``` + +``` + * + * +* +``` + +This one is known as **glider**: + +``` +*** +* + * +``` + ## Instructions to the port Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) From 159aa46e2126799991e4229c78076ef3fcc994b3 Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 16:04:14 -0300 Subject: [PATCH 04/14] Fixed pattern reading when inputing DONE, which would lead to incorrect sizing of the pattern transcribed to the matrix and caused drifting in relation to the original. --- 55_Life/csharp/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/55_Life/csharp/Program.cs b/55_Life/csharp/Program.cs index 1b70ed3a..ffdf1799 100644 --- a/55_Life/csharp/Program.cs +++ b/55_Life/csharp/Program.cs @@ -23,7 +23,6 @@ IEnumerable ReadPattern(int limitHeight) var input = Console.ReadLine(); if (input.ToUpper() == "DONE") { - yield return string.Empty; break; } From 73665d8b091f4dbf8bee2101d572c99c75909cb7 Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 16:04:57 -0300 Subject: [PATCH 05/14] Fixes cropping that would happen when using an dot (.) in the beggining of the text. --- 55_Life/csharp/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/55_Life/csharp/Program.cs b/55_Life/csharp/Program.cs index ffdf1799..f5bb7f38 100644 --- a/55_Life/csharp/Program.cs +++ b/55_Life/csharp/Program.cs @@ -30,7 +30,7 @@ IEnumerable ReadPattern(int limitHeight) // game allowed you to input an '.' before the spaces to circumvent this limitation. This behavior was // kept for compatibility. if (input.StartsWith('.')) - yield return input.Substring(1, input.Length - 2); + yield return input.Substring(1, input.Length - 1); yield return input; } From f25adca07a0f50619835097ae460ef97af46f078 Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 16:07:35 -0300 Subject: [PATCH 06/14] Fix the initialization of the matrix, which was displacing the pattern in the initial position onto the matrix, which caused the evolution of the simulation to variate in relation with the original game in Basic when once the cells reached the boarder (invalid cases). --- 55_Life/csharp/Program.cs | 48 +++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/55_Life/csharp/Program.cs b/55_Life/csharp/Program.cs index f5bb7f38..3aeb088e 100644 --- a/55_Life/csharp/Program.cs +++ b/55_Life/csharp/Program.cs @@ -4,11 +4,12 @@ const int maxWidth = 70; const int maxHeight = 24; Console.WriteLine("ENTER YOUR PATTERN:"); -var pattern = ReadPattern(limitHeight: maxHeight).ToArray(); +var pattern = new Pattern(ReadPattern(limitHeight: maxHeight).ToArray()); -var (minX, minY) = FindTopLeftCorner(pattern); -var maxX = maxHeight; -var maxY = maxWidth; +var minX = 10 - pattern.Height / 2; // was 11 +var minY = 32 - pattern.Width / 2; // was 33 +var maxX = maxHeight - 1; +var maxY = maxWidth - 1; var matrix = new Matrix(height: maxHeight, width: maxWidth); var simulation = InitializeSimulation(pattern, matrix); @@ -36,17 +37,6 @@ IEnumerable ReadPattern(int limitHeight) } } -(int minX, int minY) FindTopLeftCorner(IEnumerable patternLines) -{ - var longestInput = patternLines - .Select((value, index) => (index, value)) - .OrderByDescending(input => input.value.Length) - .First(); - var centerX = (11 - longestInput.index / 2) - 1; - var centerY = (33 - longestInput.value.Length / 2) - 1; - return (centerX, centerY); -} - void PrintHeader() { void PrintCentered(string text) @@ -65,15 +55,16 @@ void PrintHeader() Console.WriteLine(); } -Simulation InitializeSimulation(IReadOnlyList inputPattern, Matrix matrixToInitialize) { +Simulation InitializeSimulation(Pattern pattern, Matrix matrixToInitialize) { var newSimulation = new Simulation(); - // translates the pattern to the middle of the simulation and counts initial population - for (var x = 0; x < inputPattern.Count; x++) + // transcribes the pattern to the middle of the simulation and counts initial population + for (var x = 0; x < pattern.Height; x++) { - for (var y = 0; y < inputPattern[x].Length; y++) + for (var y = 0; y < pattern.Width; y++) { - if (inputPattern[x][y] == ' ') continue; + if (pattern.Content[x][y] == ' ') + continue; matrixToInitialize[minX + x, minY + y] = CellState.Stable; newSimulation.IncreasePopulation(); @@ -247,6 +238,23 @@ void ProcessSimulation() } } +public class Pattern +{ + public string[] Content { get; } + public int Height { get; } + public int Width { get; } + + public Pattern(IReadOnlyCollection patternLines) + { + Height = patternLines.Count; + Width = patternLines.Max(x => x.Length); + + Content = patternLines + .Select(x => x.PadRight(Width, ' ')) + .ToArray(); + } +} + /// /// Indicates the state of a given cell in the simulation. /// From 6a3f0b3259112a3fd8589c66ad249805cbae68f9 Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 16:09:10 -0300 Subject: [PATCH 07/14] Fix various indexing problems that caused drifting. Now, the application behaves exactly like the original, even in "invalid" generations. --- 55_Life/csharp/Program.cs | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/55_Life/csharp/Program.cs b/55_Life/csharp/Program.cs index 3aeb088e..a6fb0d98 100644 --- a/55_Life/csharp/Program.cs +++ b/55_Life/csharp/Program.cs @@ -117,10 +117,10 @@ void ProcessSimulation() } // refreshes the matrix and updates search area - for (var x = minX; x < maxX; x++) + for (var x = minX; x <= maxX; x++) { var printedLine = Enumerable.Repeat(' ', maxWidth).ToList(); - for (var y = minY; y < maxY; y++) + for (var y = minY; y <= maxY; y++) { if (matrix[x, y] == CellState.Dying) { @@ -139,9 +139,9 @@ void ProcessSimulation() printedLine[y] = '*'; nextMinX = Math.Min(x, nextMinX); - nextMaxX = Math.Max(x + 1, nextMaxX); + nextMaxX = Math.Max(x, nextMaxX); nextMinY = Math.Min(y, nextMinY); - nextMaxY = Math.Max(y + 1, nextMaxY); + nextMaxY = Math.Max(y, nextMaxY); } matrixOutput.AppendLine(string.Join(separator: null, values: printedLine)); @@ -162,42 +162,45 @@ void ProcessSimulation() minY = nextMinY; maxY = nextMaxY; - if (minX < 3) + if (minX < 2) // was 3 { - minX = 3; + minX = 2; // was 3 isInvalid = true; } - if (maxX > 22) + const int limitX = 22; //maxHeight - 2; // was 22 + const int limitY = 68; //maxWidth - 2; // was 68 + + if (maxX > limitX) // was 22 { - maxX = 22; + maxX = limitX; // was 22 isInvalid = true; } - if (minY < 3) + if (minY < 2) // was 3 { - minY = 3; + minY = 2; // was 3 isInvalid = true; } - if (maxY > 68) + if (maxY > limitY) // was 68 { - maxY = 68; + maxY = limitY; // was 68 isInvalid = true; } } UpdateSearchArea(); - for (var x = minX - 1; x < maxX + 2; x++) + for (var x = minX - 1; x <= maxX + 1; x++) { - for (var y = minY - 1; y < maxY + 2; y++) + for (var y = minY - 1; y <= maxY + 1; y++) { int CountNeighbors() { var neighbors = 0; - for (var i = x - 1; i < x + 2; i++) + for (var i = x - 1; i <= x + 1; i++) { - for (var j = y - 1; j < y + 2; j++) + for (var j = y - 1; j <= y + 1; j++) { if (matrix[i, j] == CellState.Stable || matrix[i, j] == CellState.Dying) neighbors++; @@ -208,7 +211,7 @@ void ProcessSimulation() } var neighbors = CountNeighbors(); - if (matrix[x, y] == 0) + if (matrix[x, y] == CellState.Empty) { if (neighbors == 3) { From fd8c02371a272d3d64777c6c1e5de8fdd6af462d Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 19:49:30 -0300 Subject: [PATCH 08/14] Adjusting indexes. --- 55_Life/csharp/Program.cs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/55_Life/csharp/Program.cs b/55_Life/csharp/Program.cs index a6fb0d98..d2cf8c1c 100644 --- a/55_Life/csharp/Program.cs +++ b/55_Life/csharp/Program.cs @@ -162,30 +162,30 @@ void ProcessSimulation() minY = nextMinY; maxY = nextMaxY; - if (minX < 2) // was 3 - { - minX = 2; // was 3 - isInvalid = true; - } - - const int limitX = 22; //maxHeight - 2; // was 22 - const int limitY = 68; //maxWidth - 2; // was 68 + const int limitX = 21; + const int limitY = 67; - if (maxX > limitX) // was 22 + if (minX < 2) { - maxX = limitX; // was 22 + minX = 2; + isInvalid = true; + } + + if (maxX > limitX) + { + maxX = limitX; isInvalid = true; } - if (minY < 2) // was 3 + if (minY < 2) { - minY = 2; // was 3 + minY = 2; isInvalid = true; } - if (maxY > limitY) // was 68 + if (maxY > limitY) { - maxY = limitY; // was 68 + maxY = limitY; isInvalid = true; } } From 5731a4df0823a6cc7d5337325d49f0b4ebf03ed0 Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 19:50:32 -0300 Subject: [PATCH 09/14] Temporary compensation for error calculating (possibly related to rounding) that caused misplacement of the initial pattern by 2 in the y axis. --- 55_Life/csharp/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/55_Life/csharp/Program.cs b/55_Life/csharp/Program.cs index d2cf8c1c..bb0463b4 100644 --- a/55_Life/csharp/Program.cs +++ b/55_Life/csharp/Program.cs @@ -66,7 +66,7 @@ Simulation InitializeSimulation(Pattern pattern, Matrix matrixToInitialize) { if (pattern.Content[x][y] == ' ') continue; - matrixToInitialize[minX + x, minY + y] = CellState.Stable; + matrixToInitialize[minX + x, minY + y + 2] = CellState.Stable; newSimulation.IncreasePopulation(); } } From 7a7d92ce2448517329eed7c62891415e9ffa4a35 Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 20:07:52 -0300 Subject: [PATCH 10/14] Compensated for the displacement that was occurring in the y axis by adjusting the "middle" to a valid value when working with zero based indexes. --- 55_Life/csharp/Program.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/55_Life/csharp/Program.cs b/55_Life/csharp/Program.cs index bb0463b4..32514c75 100644 --- a/55_Life/csharp/Program.cs +++ b/55_Life/csharp/Program.cs @@ -6,8 +6,8 @@ const int maxHeight = 24; Console.WriteLine("ENTER YOUR PATTERN:"); var pattern = new Pattern(ReadPattern(limitHeight: maxHeight).ToArray()); -var minX = 10 - pattern.Height / 2; // was 11 -var minY = 32 - pattern.Width / 2; // was 33 +var minX = 10 - pattern.Height / 2; +var minY = 34 - pattern.Width / 2; var maxX = maxHeight - 1; var maxY = maxWidth - 1; @@ -66,7 +66,7 @@ Simulation InitializeSimulation(Pattern pattern, Matrix matrixToInitialize) { if (pattern.Content[x][y] == ' ') continue; - matrixToInitialize[minX + x, minY + y + 2] = CellState.Stable; + matrixToInitialize[minX + x, minY + y] = CellState.Stable; newSimulation.IncreasePopulation(); } } From f70b6d42ddac55660817f0357849bebcd016623d Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 20:08:53 -0300 Subject: [PATCH 11/14] Refactoring. --- 55_Life/csharp/Program.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/55_Life/csharp/Program.cs b/55_Life/csharp/Program.cs index 32514c75..8da15360 100644 --- a/55_Life/csharp/Program.cs +++ b/55_Life/csharp/Program.cs @@ -251,8 +251,12 @@ public class Pattern { Height = patternLines.Count; Width = patternLines.Max(x => x.Length); - - Content = patternLines + Content = NormalizeWidth(patternLines); + } + + private string[] NormalizeWidth(IReadOnlyCollection patternLines) + { + return patternLines .Select(x => x.PadRight(Width, ' ')) .ToArray(); } From d52981de7313a81ecca15e8d7176623bba266fdb Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 20:21:30 -0300 Subject: [PATCH 12/14] Refactoring. --- 55_Life/csharp/Program.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/55_Life/csharp/Program.cs b/55_Life/csharp/Program.cs index 8da15360..879b16be 100644 --- a/55_Life/csharp/Program.cs +++ b/55_Life/csharp/Program.cs @@ -76,15 +76,14 @@ Simulation InitializeSimulation(Pattern pattern, Matrix matrixToInitialize) { TimeSpan GetPauseBetweenIterations() { - if (args.Length == 2) + if (args.Length != 2) return TimeSpan.Zero; + + var parameter = args[0].ToLower(); + if (parameter.Contains("wait")) { - var parameter = args[0].ToLower(); - if (parameter.Contains("wait")) - { - var value = args[1]; - if (int.TryParse(value, out var sleepMilliseconds)) - return TimeSpan.FromMilliseconds(sleepMilliseconds); - } + var value = args[1]; + if (int.TryParse(value, out var sleepMilliseconds)) + return TimeSpan.FromMilliseconds(sleepMilliseconds); } return TimeSpan.Zero; From b93cc409e204f5f96d496364416a4c49fec9e04a Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 20:22:26 -0300 Subject: [PATCH 13/14] Adjusted message of "invalid" generations, matching the original. --- 55_Life/csharp/Program.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/55_Life/csharp/Program.cs b/55_Life/csharp/Program.cs index 879b16be..bcfc22f4 100644 --- a/55_Life/csharp/Program.cs +++ b/55_Life/csharp/Program.cs @@ -96,9 +96,8 @@ void ProcessSimulation() while (true) { - Console.WriteLine($"GENERATION: {simulation.Generation}\tPOPULATION: {simulation.Population}"); - if (isInvalid) - Console.WriteLine("INVALID!"); + var invalidText = isInvalid ? "INVALID!" : ""; + Console.WriteLine($"GENERATION: {simulation.Generation}\tPOPULATION: {simulation.Population} {invalidText}"); simulation.StartNewGeneration(); From 2ac28191511259a6a9f3056a8766eb6b95c1ab61 Mon Sep 17 00:00:00 2001 From: Dyego Maas Date: Wed, 12 Jan 2022 20:23:17 -0300 Subject: [PATCH 14/14] Removed extra line printed after each generation, to better match the original's visuals. --- 55_Life/csharp/Program.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/55_Life/csharp/Program.cs b/55_Life/csharp/Program.cs index bcfc22f4..eeb00465 100644 --- a/55_Life/csharp/Program.cs +++ b/55_Life/csharp/Program.cs @@ -150,8 +150,7 @@ void ProcessSimulation() { matrixOutput.AppendLine(); } - Console.WriteLine(matrixOutput); - Console.WriteLine(); + Console.Write(matrixOutput); void UpdateSearchArea() {