From 2f9e70757728d69b4c032d2d0d07f3e0a7bc223d Mon Sep 17 00:00:00 2001 From: jbtule Date: Wed, 9 Sep 2020 23:07:52 -0500 Subject: [PATCH 01/11] Enumoji implementation in C# 14.0 --- .gitignore | 3 +- GameOfLife.sln | 2 +- .../{GameOfLife.csproj => GameOfLife.fsproj} | 3 + GameOfLife/Program.cs | 125 ------------------ GameOfLife/Program.fs | 67 ++++++++++ 5 files changed, 73 insertions(+), 127 deletions(-) rename GameOfLife/{GameOfLife.csproj => GameOfLife.fsproj} (71%) delete mode 100644 GameOfLife/Program.cs create mode 100644 GameOfLife/Program.fs diff --git a/.gitignore b/.gitignore index 91953df..42f5518 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ obj/ /packages/ riderModule.iml /_ReSharper.Caches/ -.idea \ No newline at end of file +.idea +.ionide diff --git a/GameOfLife.sln b/GameOfLife.sln index fbb78e5..ce65135 100644 --- a/GameOfLife.sln +++ b/GameOfLife.sln @@ -1,6 +1,6 @@ ๏ปฟ Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameOfLife", "GameOfLife\GameOfLife.csproj", "{980E6F7A-195D-4F06-9398-B07DD274D20A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameOfLife", "GameOfLife\GameOfLife.fsproj", "{980E6F7A-195D-4F06-9398-B07DD274D20A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/GameOfLife/GameOfLife.csproj b/GameOfLife/GameOfLife.fsproj similarity index 71% rename from GameOfLife/GameOfLife.csproj rename to GameOfLife/GameOfLife.fsproj index 2c0dacc..3922103 100644 --- a/GameOfLife/GameOfLife.csproj +++ b/GameOfLife/GameOfLife.fsproj @@ -4,5 +4,8 @@ Exe netcoreapp3.1 + + + diff --git a/GameOfLife/Program.cs b/GameOfLife/Program.cs deleted file mode 100644 index 0564248..0000000 --- a/GameOfLife/Program.cs +++ /dev/null @@ -1,125 +0,0 @@ -๏ปฟusing System; -using System.Security.Cryptography; -using System.Text; -using System.Threading; - -namespace GameOfLife -{ - static class Program - { - const int Rows = 15; - const int Columns = 15; - - static bool runSimulation = true; - - public static void Main() - { - var grid = new Status[Rows, Columns]; - - // randomly initialize our grid - for (var row = 0; row < Rows; row++) - { - for (var column = 0; column < Columns; column++) - { - grid[row, column] = (Status) RandomNumberGenerator.GetInt32(0, 2); - } - } - - Console.CancelKeyPress += (sender, args) => - { - runSimulation = false; - Console.WriteLine("\n๐Ÿ‘‹ Ending simulation."); - }; - - // let's give our console - // a good scrubbing - Console.Clear(); - - // Displaying the grid - while (runSimulation) - { - Print(grid); - grid = NextGeneration(grid); - } - } - - private static Status[,] NextGeneration(Status[,] currentGrid) - { - var nextGeneration = new Status[Rows, Columns]; - - // Loop through every cell - for (var row = 1; row < Rows - 1; row++) - for (var column = 1; column < Columns - 1; column++) - { - // find your alive neighbors - var aliveNeighbors = 0; - for (var i = -1; i <= 1; i++) - { - for (var j = -1; j <= 1; j++) - { - aliveNeighbors += currentGrid[row + i, column + j] == Status.Alive ? 1 : 0; - } - } - - var currentCell = currentGrid[row, column]; - - // The cell needs to be subtracted - // from its neighbours as it was - // counted before - aliveNeighbors -= currentCell == Status.Alive ? 1 : 0; - - // Implementing the Rules of Life - - // Cell is lonely and dies - if (currentCell.HasFlag(Status.Alive) && aliveNeighbors < 2) - { - nextGeneration[row,column] = Status.Dead; - } - - // Cell dies due to over population - else if (currentCell.HasFlag(Status.Alive) && aliveNeighbors > 3) - { - nextGeneration[row, column] = Status.Dead; - } - - // A new cell is born - else if (currentCell.HasFlag(Status.Dead) && aliveNeighbors == 3) - { - nextGeneration[row,column] = Status.Alive; - } - // stays the same - else - { - nextGeneration[row, column] = currentCell; - } - } - return nextGeneration; - } - - private static void Print(Status[,] future, int timeout = 500) - { - var stringBuilder = new StringBuilder(); - for (var row = 0; row < Rows; row++) - { - for (var column = 0; column < Columns; column++) - { - var cell = future[row, column]; - stringBuilder.Append(cell == Status.Alive ? "๐Ÿง‘โ€๐Ÿš€" : "๐Ÿ‘ฝ"); - } - stringBuilder.Append("\n"); - } - - Console.BackgroundColor = ConsoleColor.Black; - Console.CursorVisible = false; - Console.SetCursorPosition(0, 0); - Console.Write(stringBuilder.ToString()); - Thread.Sleep(timeout); - } - } - - public enum Status - { - Dead, - Alive, - } -} \ No newline at end of file diff --git a/GameOfLife/Program.fs b/GameOfLife/Program.fs new file mode 100644 index 0000000..da7e4b5 --- /dev/null +++ b/GameOfLife/Program.fs @@ -0,0 +1,67 @@ +module GameOfLife.Program +open System +open System.Security.Cryptography +open System.Text +open System.Threading + +let rows = 15 +let columns = 15 +let timeout = 500 +let mutable runSimulation = true + +type Status = ``๐Ÿ’€`` = 0 | ``๐Ÿ˜`` = 1 + +let private nextGeneration (currentGrid: Status [,]) = + let nextGeneration = Array2D.zeroCreate rows columns + // Loop through every cell + for row in 1..(rows-2) do + for column in 1..(columns-2) do + // find your alive neighbor + let mutable ``๐Ÿ˜Neighbors`` = 0 + for i in -1..1 do + for j in -1..1 do + ``๐Ÿ˜Neighbors`` <- ``๐Ÿ˜Neighbors`` + int currentGrid.[row+i, column+i] + let currentCell = currentGrid.[row, column] + // The cell needs to be subtracted + // from its neighbours as it was + // counted before + ``๐Ÿ˜Neighbors`` <- ``๐Ÿ˜Neighbors`` - int currentCell + // Implementing the Rules of Life + nextGeneration.[row,column] <- + match currentCell with + // Cell is lonely and dies OR Cell dies due to over population + | Status.``๐Ÿ˜`` when ``๐Ÿ˜Neighbors`` < 2 || ``๐Ÿ˜Neighbors`` > 3 -> Status.``๐Ÿ’€`` + // A new cell is born + | Status.``๐Ÿ’€`` when ``๐Ÿ˜Neighbors`` = 3 -> Status.``๐Ÿ˜`` + // stays the same + | _ -> currentCell + nextGeneration + +let private print (future: Status[,]) = + let sb = StringBuilder() + for row in 0..(rows-1) do + for column in 0..(columns-1) do + future.[row, column] |> string |> sb.Append |> ignore + sb.AppendLine() |> ignore + Console.BackgroundColor <- ConsoleColor.Black + Console.CursorVisible <- false + Console.SetCursorPosition(0,0) + sb.ToString() |> Console.Write |> ignore + Thread.Sleep(timeout) + +[] +let main _ = + // randomly initialize our grid + let mutable grid = Array2D.init rows columns (fun _ _ -> RandomNumberGenerator.GetInt32(0, 2) |> enum) + Console.CancelKeyPress.Add( + fun _ -> + runSimulation <- false + Console.WriteLine("\n๐Ÿ‘‹ Ending simulation.")) + // let's give our console + // a good scrubbing + Console.Clear(); + // Displaying the grid + while runSimulation do + print grid + grid <- nextGeneration(grid) + 0 From e6cdbb0896154ac5e10ba505aaf57c9fef514f2e Mon Sep 17 00:00:00 2001 From: Jay Tuley Date: Sat, 12 Sep 2020 12:59:18 -0500 Subject: [PATCH 02/11] removing emoji from variable name --- GameOfLife/Program.fs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/GameOfLife/Program.fs b/GameOfLife/Program.fs index da7e4b5..bf88209 100644 --- a/GameOfLife/Program.fs +++ b/GameOfLife/Program.fs @@ -17,22 +17,22 @@ let private nextGeneration (currentGrid: Status [,]) = for row in 1..(rows-2) do for column in 1..(columns-2) do // find your alive neighbor - let mutable ``๐Ÿ˜Neighbors`` = 0 + let mutable aliveNeighbors = 0 for i in -1..1 do for j in -1..1 do - ``๐Ÿ˜Neighbors`` <- ``๐Ÿ˜Neighbors`` + int currentGrid.[row+i, column+i] + aliveNeighbors <- aliveNeighbors + int currentGrid.[row+i, column+i] let currentCell = currentGrid.[row, column] // The cell needs to be subtracted // from its neighbours as it was // counted before - ``๐Ÿ˜Neighbors`` <- ``๐Ÿ˜Neighbors`` - int currentCell + aliveNeighbors <- aliveNeighbors - int currentCell // Implementing the Rules of Life nextGeneration.[row,column] <- match currentCell with // Cell is lonely and dies OR Cell dies due to over population - | Status.``๐Ÿ˜`` when ``๐Ÿ˜Neighbors`` < 2 || ``๐Ÿ˜Neighbors`` > 3 -> Status.``๐Ÿ’€`` + | Status.``๐Ÿ˜`` when aliveNeighbors < 2 || aliveNeighbors > 3 -> Status.``๐Ÿ’€`` // A new cell is born - | Status.``๐Ÿ’€`` when ``๐Ÿ˜Neighbors`` = 3 -> Status.``๐Ÿ˜`` + | Status.``๐Ÿ’€`` when aliveNeighbors = 3 -> Status.``๐Ÿ˜`` // stays the same | _ -> currentCell nextGeneration From 91a01e10eea22bdaa0f048e79c34a81669ea193b Mon Sep 17 00:00:00 2001 From: jbtule Date: Sat, 12 Sep 2020 13:00:44 -0500 Subject: [PATCH 03/11] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 42f5518..9b027c2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ riderModule.iml /_ReSharper.Caches/ .idea .ionide +.fake \ No newline at end of file From 0e784003cde391cb3d027cbd0a61965a4538d6de Mon Sep 17 00:00:00 2001 From: jbtule Date: Sat, 12 Sep 2020 13:04:26 -0500 Subject: [PATCH 04/11] enum type inferrable --- GameOfLife/Program.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GameOfLife/Program.fs b/GameOfLife/Program.fs index bf88209..65993a9 100644 --- a/GameOfLife/Program.fs +++ b/GameOfLife/Program.fs @@ -52,7 +52,7 @@ let private print (future: Status[,]) = [] let main _ = // randomly initialize our grid - let mutable grid = Array2D.init rows columns (fun _ _ -> RandomNumberGenerator.GetInt32(0, 2) |> enum) + let mutable grid = Array2D.init rows columns (fun _ _ -> RandomNumberGenerator.GetInt32(0, 2) |> enum) Console.CancelKeyPress.Add( fun _ -> runSimulation <- false From ed482990fc868b2c8903586e144bc9b450b2b7bc Mon Sep 17 00:00:00 2001 From: jbtule Date: Sat, 12 Sep 2020 13:29:31 -0500 Subject: [PATCH 05/11] not discarding i think reads better --- GameOfLife/Program.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GameOfLife/Program.fs b/GameOfLife/Program.fs index 65993a9..c7c3188 100644 --- a/GameOfLife/Program.fs +++ b/GameOfLife/Program.fs @@ -34,7 +34,7 @@ let private nextGeneration (currentGrid: Status [,]) = // A new cell is born | Status.``๐Ÿ’€`` when aliveNeighbors = 3 -> Status.``๐Ÿ˜`` // stays the same - | _ -> currentCell + | unchanged -> unchanged nextGeneration let private print (future: Status[,]) = From 2d9004d293b47f370e1d01b46cd2989507d76515 Mon Sep 17 00:00:00 2001 From: jbtule Date: Wed, 16 Sep 2020 00:39:31 -0500 Subject: [PATCH 06/11] functional refactor --- GameOfLife/Program.fs | 95 ++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 52 deletions(-) diff --git a/GameOfLife/Program.fs b/GameOfLife/Program.fs index c7c3188..b53b567 100644 --- a/GameOfLife/Program.fs +++ b/GameOfLife/Program.fs @@ -1,67 +1,58 @@ module GameOfLife.Program open System -open System.Security.Cryptography -open System.Text open System.Threading let rows = 15 let columns = 15 -let timeout = 500 -let mutable runSimulation = true +let timer = 500 type Status = ``๐Ÿ’€`` = 0 | ``๐Ÿ˜`` = 1 +type RNG = Security.Cryptography.RandomNumberGenerator -let private nextGeneration (currentGrid: Status [,]) = - let nextGeneration = Array2D.zeroCreate rows columns - // Loop through every cell - for row in 1..(rows-2) do - for column in 1..(columns-2) do - // find your alive neighbor - let mutable aliveNeighbors = 0 - for i in -1..1 do - for j in -1..1 do - aliveNeighbors <- aliveNeighbors + int currentGrid.[row+i, column+i] - let currentCell = currentGrid.[row, column] - // The cell needs to be subtracted - // from its neighbours as it was - // counted before - aliveNeighbors <- aliveNeighbors - int currentCell - // Implementing the Rules of Life - nextGeneration.[row,column] <- - match currentCell with - // Cell is lonely and dies OR Cell dies due to over population - | Status.``๐Ÿ˜`` when aliveNeighbors < 2 || aliveNeighbors > 3 -> Status.``๐Ÿ’€`` - // A new cell is born - | Status.``๐Ÿ’€`` when aliveNeighbors = 3 -> Status.``๐Ÿ˜`` - // stays the same - | unchanged -> unchanged - nextGeneration +let private nextGeneration (grid: Status [,]) = + grid + |> Array2D.mapi (fun r c -> + let aliveNeighbors = + (seq { -1 .. 1 }, seq { -1 .. 1 }) + ||> Seq.allPairs + |> Seq.map (fun (x, y) -> x + r, y + c) + |> Seq.filter (fun (x, y) -> x < rows && y < columns && x >= 0 && y >= 0) + |> Seq.sumBy (fun (x, y) -> int grid.[x, y]) + function + // Cell is lonely and dies OR Cell dies due to over population + | Status.``๐Ÿ˜`` when aliveNeighbors < 2 || aliveNeighbors > 3 -> Status.``๐Ÿ’€`` + // A new cell is born + | Status.``๐Ÿ’€`` when aliveNeighbors = 3 -> Status.``๐Ÿ˜`` + // stays the same + | unchanged -> unchanged) -let private print (future: Status[,]) = - let sb = StringBuilder() - for row in 0..(rows-1) do - for column in 0..(columns-1) do - future.[row, column] |> string |> sb.Append |> ignore - sb.AppendLine() |> ignore - Console.BackgroundColor <- ConsoleColor.Black - Console.CursorVisible <- false - Console.SetCursorPosition(0,0) - sb.ToString() |> Console.Write |> ignore - Thread.Sleep(timeout) +let private stringify (grid: Status [,]) = + grid + |> Array2D.mapi (fun _ y status -> status |> string |> if y = columns - 1 then sprintf "%s\n" else id) + |> Seq.cast + |> String.concat String.Empty [] let main _ = - // randomly initialize our grid - let mutable grid = Array2D.init rows columns (fun _ _ -> RandomNumberGenerator.GetInt32(0, 2) |> enum) - Console.CancelKeyPress.Add( - fun _ -> - runSimulation <- false - Console.WriteLine("\n๐Ÿ‘‹ Ending simulation.")) + let cts = new CancellationTokenSource() + Console.CancelKeyPress.Add(fun _ -> cts.Cancel(); Console.WriteLine "\n๐Ÿ‘‹ Ending simulation.") + //Define our async work - cold + let work = async { + // randomly initialize our grid + let mutable grid = + Array2D.init rows columns (fun _ _ -> RNG.GetInt32(0, 2) |> enum) + while true do + // Displaying the grid + Console.SetCursorPosition(0, 0) + grid |> stringify |> Console.Write + grid <- nextGeneration grid + do! Async.Sleep timer + } // let's give our console // a good scrubbing - Console.Clear(); - // Displaying the grid - while runSimulation do - print grid - grid <- nextGeneration(grid) - 0 + Console.Clear() + Console.BackgroundColor <- ConsoleColor.Black + Console.CursorVisible <- false + //Do The thing + Async.RunSynchronously(work, cancellationToken = cts.Token) + 0 \ No newline at end of file From bec3dd7bf4fad231bffb656e090baa273ddd0e6c Mon Sep 17 00:00:00 2001 From: jbtule Date: Wed, 16 Sep 2020 00:54:14 -0500 Subject: [PATCH 07/11] Neighbors could have been off by one --- GameOfLife/Program.fs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/GameOfLife/Program.fs b/GameOfLife/Program.fs index b53b567..8f8f91e 100644 --- a/GameOfLife/Program.fs +++ b/GameOfLife/Program.fs @@ -13,11 +13,13 @@ let private nextGeneration (grid: Status [,]) = grid |> Array2D.mapi (fun r c -> let aliveNeighbors = - (seq { -1 .. 1 }, seq { -1 .. 1 }) + ((seq { -1 .. 1 }, seq { -1 .. 1 }) ||> Seq.allPairs |> Seq.map (fun (x, y) -> x + r, y + c) |> Seq.filter (fun (x, y) -> x < rows && y < columns && x >= 0 && y >= 0) - |> Seq.sumBy (fun (x, y) -> int grid.[x, y]) + |> Seq.sumBy (fun (x, y) -> int grid.[x, y])) + //The cell needs to be subtracted from its neighbours as it was counted before + - (int grid.[r, c]) function // Cell is lonely and dies OR Cell dies due to over population | Status.``๐Ÿ˜`` when aliveNeighbors < 2 || aliveNeighbors > 3 -> Status.``๐Ÿ’€`` From 6b6ddb0399d5b5ec8786d5a00a0225574dee11e3 Mon Sep 17 00:00:00 2001 From: jbtule Date: Wed, 16 Sep 2020 07:14:36 -0500 Subject: [PATCH 08/11] duh didn't need to pipe to string first --- GameOfLife/Program.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GameOfLife/Program.fs b/GameOfLife/Program.fs index 8f8f91e..6e81a05 100644 --- a/GameOfLife/Program.fs +++ b/GameOfLife/Program.fs @@ -30,7 +30,7 @@ let private nextGeneration (grid: Status [,]) = let private stringify (grid: Status [,]) = grid - |> Array2D.mapi (fun _ y status -> status |> string |> if y = columns - 1 then sprintf "%s\n" else id) + |> Array2D.mapi (fun _ y status -> status |> if y = columns - 1 then sprintf "%A\n" else string) |> Seq.cast |> String.concat String.Empty From bc1dc102d8d665493dbdc064e4cd2983d07dac81 Mon Sep 17 00:00:00 2001 From: jbtule Date: Wed, 16 Sep 2020 08:12:00 -0500 Subject: [PATCH 09/11] curried form, don't need argument --- GameOfLife/Program.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GameOfLife/Program.fs b/GameOfLife/Program.fs index 6e81a05..cc2b594 100644 --- a/GameOfLife/Program.fs +++ b/GameOfLife/Program.fs @@ -30,7 +30,7 @@ let private nextGeneration (grid: Status [,]) = let private stringify (grid: Status [,]) = grid - |> Array2D.mapi (fun _ y status -> status |> if y = columns - 1 then sprintf "%A\n" else string) + |> Array2D.mapi (fun _ y -> if y = columns - 1 then sprintf "%A\n" else string) |> Seq.cast |> String.concat String.Empty From 433ceacbe35ca38a42fb1dedb922d327fa46719d Mon Sep 17 00:00:00 2001 From: jbtule Date: Wed, 16 Sep 2020 08:16:34 -0500 Subject: [PATCH 10/11] skip center rather than subtract --- GameOfLife/Program.fs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/GameOfLife/Program.fs b/GameOfLife/Program.fs index cc2b594..41a7716 100644 --- a/GameOfLife/Program.fs +++ b/GameOfLife/Program.fs @@ -15,11 +15,10 @@ let private nextGeneration (grid: Status [,]) = let aliveNeighbors = ((seq { -1 .. 1 }, seq { -1 .. 1 }) ||> Seq.allPairs + |> Seq.choose (function | (0, 0) -> None | x -> Some x) //skip center |> Seq.map (fun (x, y) -> x + r, y + c) |> Seq.filter (fun (x, y) -> x < rows && y < columns && x >= 0 && y >= 0) |> Seq.sumBy (fun (x, y) -> int grid.[x, y])) - //The cell needs to be subtracted from its neighbours as it was counted before - - (int grid.[r, c]) function // Cell is lonely and dies OR Cell dies due to over population | Status.``๐Ÿ˜`` when aliveNeighbors < 2 || aliveNeighbors > 3 -> Status.``๐Ÿ’€`` From 5406389a3e8a41c71f0428c84ea4ce096f47e054 Mon Sep 17 00:00:00 2001 From: Jay Tuley Date: Wed, 16 Sep 2020 08:47:03 -0500 Subject: [PATCH 11/11] remove redundant parens --- GameOfLife/Program.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GameOfLife/Program.fs b/GameOfLife/Program.fs index 41a7716..72ace83 100644 --- a/GameOfLife/Program.fs +++ b/GameOfLife/Program.fs @@ -13,12 +13,12 @@ let private nextGeneration (grid: Status [,]) = grid |> Array2D.mapi (fun r c -> let aliveNeighbors = - ((seq { -1 .. 1 }, seq { -1 .. 1 }) + (seq { -1 .. 1 }, seq { -1 .. 1 }) ||> Seq.allPairs |> Seq.choose (function | (0, 0) -> None | x -> Some x) //skip center |> Seq.map (fun (x, y) -> x + r, y + c) |> Seq.filter (fun (x, y) -> x < rows && y < columns && x >= 0 && y >= 0) - |> Seq.sumBy (fun (x, y) -> int grid.[x, y])) + |> Seq.sumBy (fun (x, y) -> int grid.[x, y]) function // Cell is lonely and dies OR Cell dies due to over population | Status.``๐Ÿ˜`` when aliveNeighbors < 2 || aliveNeighbors > 3 -> Status.``๐Ÿ’€`` @@ -56,4 +56,4 @@ let main _ = Console.CursorVisible <- false //Do The thing Async.RunSynchronously(work, cancellationToken = cts.Token) - 0 \ No newline at end of file + 0