diff --git a/.gitignore b/.gitignore index 91953df..9b027c2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ obj/ /packages/ riderModule.iml /_ReSharper.Caches/ -.idea \ No newline at end of file +.idea +.ionide +.fake \ No newline at end of file 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..72ace83 --- /dev/null +++ b/GameOfLife/Program.fs @@ -0,0 +1,59 @@ +module GameOfLife.Program +open System +open System.Threading + +let rows = 15 +let columns = 15 +let timer = 500 + +type Status = ``πŸ’€`` = 0 | ``😁`` = 1 +type RNG = Security.Cryptography.RandomNumberGenerator + +let private nextGeneration (grid: Status [,]) = + grid + |> Array2D.mapi (fun r c -> + 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]) + 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 stringify (grid: Status [,]) = + grid + |> Array2D.mapi (fun _ y -> if y = columns - 1 then sprintf "%A\n" else string) + |> Seq.cast + |> String.concat String.Empty + +[] +let main _ = + 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() + Console.BackgroundColor <- ConsoleColor.Black + Console.CursorVisible <- false + //Do The thing + Async.RunSynchronously(work, cancellationToken = cts.Token) + 0