diff --git a/GameOfLife.sln b/GameOfLife.sln new file mode 100644 index 0000000..dbcd493 --- /dev/null +++ b/GameOfLife.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35327.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameOfLife", "GameOfLife\GameOfLife.csproj", "{82A1D4AF-7CCF-4328-8A9A-BA7EEC2889E8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SFMLGame", "SFMLGame\SFMLGame.csproj", "{0B3283B0-16CB-4E2F-B199-6155C3CB6AC0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {82A1D4AF-7CCF-4328-8A9A-BA7EEC2889E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {82A1D4AF-7CCF-4328-8A9A-BA7EEC2889E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {82A1D4AF-7CCF-4328-8A9A-BA7EEC2889E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {82A1D4AF-7CCF-4328-8A9A-BA7EEC2889E8}.Release|Any CPU.Build.0 = Release|Any CPU + {0B3283B0-16CB-4E2F-B199-6155C3CB6AC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B3283B0-16CB-4E2F-B199-6155C3CB6AC0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B3283B0-16CB-4E2F-B199-6155C3CB6AC0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B3283B0-16CB-4E2F-B199-6155C3CB6AC0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {41116F8D-C3BE-458F-94EF-E56DA4303519} + EndGlobalSection +EndGlobal diff --git a/GameOfLife/Game.cs b/GameOfLife/Game.cs new file mode 100644 index 0000000..2b0fe9a --- /dev/null +++ b/GameOfLife/Game.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameOfLife +{ + public class Game + { + public Game(int x, int y) { + ActiveMap = new(x, y); + SwapMap = new(x, y); + } + public int Iteration => _Iteration; + private GameMap ActiveMap, SwapMap; + private int _Iteration; + + + public void Update() + { + //SwapMap.Clear(); + + foreach ( var cell in ActiveMap.GetAllCells()) + { + cell.Update(ref ActiveMap, ref SwapMap); + } + _Iteration++; + + //Swap Maps + ActiveMap.CloneFrom(SwapMap); + } + + public GameMap getActiveMap() + { + return ActiveMap; + } + + public string ToString() + { + return $""" + Current Iteration: {Iteration} + """; + } + + public string RenderToString() + { + int MapRowSize = ActiveMap.SizeY; + int row = 0; + int rowCounter = 0; + StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.Append("\t0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5\n"); + stringBuilder.AppendLine(); + stringBuilder.Append("0\t"); + + foreach ( var cell in ActiveMap.GetAllCells()) + { + stringBuilder.Append(cell.ToString() + " "); + + rowCounter++; + if (rowCounter == MapRowSize) + { + rowCounter = 0; + row++; + stringBuilder.Append($"\n{row}\t"); + } + } + return stringBuilder.ToString(); + } + + public string RenderToDebugString() + { + int row = 0; + int MapRowSize = (int)Math.Sqrt(ActiveMap.GetAllCells().Length); + int rowCounter = 0; + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.Append("\t0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5\n"); + stringBuilder.AppendLine(); + stringBuilder.Append("0\t"); + foreach (var cell in ActiveMap.GetAllCells()) + { + stringBuilder.Append(cell.GetNeighborVal(ref ActiveMap) + " "); + + + rowCounter++; + if (rowCounter == MapRowSize) + { + rowCounter = 0; + row++; + stringBuilder.Append($"\n{row}\t"); + } + } + return stringBuilder.ToString(); + } + + public string RenderToSwapDebugString() + { + int MapRowSize = (int)Math.Sqrt(SwapMap.GetAllCells().Length); + int rowCounter = 0; + StringBuilder stringBuilder = new StringBuilder(); + foreach (var cell in SwapMap.GetAllCells()) + { + stringBuilder.Append(cell.GetNeighborVal(ref SwapMap)); + + rowCounter++; + if (rowCounter == MapRowSize) + { + rowCounter = 0; + stringBuilder.Append("\n"); + } + } + return stringBuilder.ToString(); + } + + + public void SetRandomStart(int XSize, int YSize, int Seed = 0,int mod = 2) + { + //Not using Size args right now + for (int i = 0; i < ActiveMap.SizeX - 1; i++) + { + for (int j = 0; j < ActiveMap.SizeY - 1; j++) + { + ActiveMap.GetCell(i,j).isAlive = (Random.Shared.Next()%mod == 0); + } + } + } + + public void GeneraterSwitcher() + { + ActiveMap.GetCell(ActiveMap.SizeX/2, ActiveMap.SizeY/2).isAlive = true; + ActiveMap.GetCell(ActiveMap.SizeX / 2, (ActiveMap.SizeY / 2)+1).isAlive = true; + ActiveMap.GetCell(ActiveMap.SizeX / 2, (ActiveMap.SizeY / 2)-1).isAlive = true; + + } + } +} diff --git a/GameOfLife/GameMap.cs b/GameOfLife/GameMap.cs new file mode 100644 index 0000000..1671793 --- /dev/null +++ b/GameOfLife/GameMap.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameOfLife +{ + public class GameMap + { + private int _SizeX, _SizeY; + + CellState[,] Cells = new CellState[1,1]; + + public int SizeX { get => _SizeX; set => _SizeX = value; } + public int SizeY { get => _SizeY; set => _SizeY = value; } + + + public CellState[,] GetAllCells() => Cells; + public GameMap(int xSize, int ySize) + { + _SizeX = xSize; + _SizeY = ySize; + InitializeCells(); + } + + public ref CellState GetCell(int x, int y) + { + + + //if (x > SizeX) { x = SizeX; } + //if (x < 0) { x = 0; } + //if (y > SizeY) { y = SizeY; } + //if (y < 0) { y = 0; } + + //Trying a wrap around + if(x >= SizeX) { x = 0; } + if(y >= SizeY) { y = 0; } + if(x < 0) { x = SizeX-1; } + if(y < 0) { y = SizeY-1; } + + + return ref Cells[x, y]; + } + + private void InitializeCells() + { + Cells = new CellState[SizeX,SizeY]; + for (int i = 0; i < Cells.GetLength(0) -1; i++) + { + for (int j = 0; j < Cells.GetLength(1) -1; j++) + { + Cells[i, j].isAlive = false; + Cells[i, j].XCoord = i; + Cells[i, j].YCoord = j; + } + } + } + + public void CloneFrom(GameMap source) + { + for(int i = 0;i < Cells.GetLength(0) - 1;i++) + { + for(int j = 0; j < Cells.GetLength(1) -1; j++) + { + this.Cells[i,j] = source.Cells[i,j]; + } + } + } + + public int GetAliveCells() + { + int alive = 0; + foreach (var cell in Cells) + { if (cell.isAlive) alive++; } + return alive; + } + + internal void Clear() + { + throw new NotImplementedException(); + } + } + + public struct CellState + { + public bool isAlive; + public int XCoord, YCoord; + + + + //Debug Function + public int GetNeighborVal(ref GameMap map) + { + return GetAliveNeighbors(ref map); + } + + + public void Update(ref GameMap CurrentMap, ref GameMap SwapMap) + { + int AliveNeighborCounter = GetAliveNeighbors(ref CurrentMap); + + //Generate new Cell + if (AliveNeighborCounter == 3) + { + SwapMap.GetCell(XCoord, YCoord).isAlive = true; + return; + } + //State unchanged + if (AliveNeighborCounter == 2) + { + SwapMap.GetCell(XCoord, YCoord).isAlive = this.isAlive; + return; + } + //Cell Dies + else + { + SwapMap.GetCell(XCoord, YCoord).isAlive = false; + } + + } + + private int GetAliveNeighbors(ref GameMap CurrentMap) + { + int AliveNeighborCounter = 0; + + // 0 0 0 + // 0 C x + // 0 0 0 + if (CurrentMap.GetCell(this.XCoord + 1, this.YCoord).isAlive) + { AliveNeighborCounter++; } + // 0 0 0 + // 0 C 0 + // 0 0 x + if (CurrentMap.GetCell(this.XCoord + 1, this.YCoord - 1).isAlive) + { AliveNeighborCounter++; } + // 0 0 0 + // 0 C 0 + // 0 x 0 + if (CurrentMap.GetCell(this.XCoord, this.YCoord - 1).isAlive) + { AliveNeighborCounter++; } + // 0 0 0 + // 0 C 0 + // x 0 0 + if (CurrentMap.GetCell(this.XCoord - 1, this.YCoord - 1).isAlive) + { AliveNeighborCounter++; } + + if (AliveNeighborCounter > 3) return AliveNeighborCounter; + // 0 0 0 + // x C 0 + // 0 0 0 + if (CurrentMap.GetCell(this.XCoord - 1, this.YCoord).isAlive) + { AliveNeighborCounter++; } + + if (AliveNeighborCounter > 3) return AliveNeighborCounter; + // x 0 0 + // 0 C 0 + // 0 0 0 + if (CurrentMap.GetCell(this.XCoord - 1, this.YCoord + 1).isAlive) + { AliveNeighborCounter++; } + + if (AliveNeighborCounter > 3) return AliveNeighborCounter; + // 0 x 0 + // 0 C 0 + // 0 0 0 + if (CurrentMap.GetCell(this.XCoord, this.YCoord + 1).isAlive) + { AliveNeighborCounter++; } + + if (AliveNeighborCounter > 3) return AliveNeighborCounter; + // 0 0 x + // 0 C 0 + // 0 0 0 + if (CurrentMap.GetCell(this.XCoord + 1, this.YCoord + 1).isAlive) + { AliveNeighborCounter++; } + + return AliveNeighborCounter; + } + + public string ToString() + { + return isAlive ? "1" : "0"; + } + + + + internal void SetAlive() + { + this.isAlive = true; + } + } +} diff --git a/GameOfLife/GameOfLife.csproj b/GameOfLife/GameOfLife.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/GameOfLife/GameOfLife.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/GameOfLife/Program.cs b/GameOfLife/Program.cs new file mode 100644 index 0000000..5164a56 --- /dev/null +++ b/GameOfLife/Program.cs @@ -0,0 +1,77 @@ +using GameOfLife; +using System.ComponentModel.Design; +using System.Diagnostics; +public class Program +{ + public async static Task Main() + { + Console.WriteLine("Hello"); + + + Game game = new(80, 80); + + game.SetRandomStart(80, 80); + //game.GeneraterSwitcher(); + Console.Write(game.RenderToDebugString()); + Console.WriteLine(); + Console.Write(game.RenderToSwapDebugString()); + //Console.ReadKey(); + int fpsct = 0; + int CurrentFPS = 0; + Stopwatch Start = new(); + Start.Start(); + PeriodicTimer timer = new(TimeSpan.FromMilliseconds(10)); + Console.ForegroundColor = ConsoleColor.DarkGreen; + while (await timer.WaitForNextTickAsync()) + { + if (Start.ElapsedMilliseconds > 1000) + { + CurrentFPS = fpsct; + fpsct = 0; + Start.Restart(); + } + fpsct++; + game.Update(); + Console.Clear(); + Console.WriteLine($"FPS: {CurrentFPS}\t Iteration: {game.Iteration}"); + Console.Write(game.RenderToString()); + if (Console.KeyAvailable) + { + if (Console.ReadKey(true).Key == ConsoleKey.R) + { + game = new(80, 80); + game.SetRandomStart(80, 80); + + } + } + //Console.Write(game.RenderToDebugString()); + //Console.WriteLine(); + //Console.Write(game.RenderToSwapDebugString()); + } + + + + + + } + + public static void ColoredWrite(string input) + { + foreach (var ch in input) + { + if (ch == '1') + { + Console.ForegroundColor = ConsoleColor.Black; + Console.Write(ch); + continue; + } + else + { + Console.Write(ch); + + } + } + + } + +} \ No newline at end of file diff --git a/SFMLGame/Program.cs b/SFMLGame/Program.cs new file mode 100644 index 0000000..29a10d9 --- /dev/null +++ b/SFMLGame/Program.cs @@ -0,0 +1,304 @@ +using GameOfLife; +using Microsoft.VisualBasic; +using SFML.Graphics; +using SFML.System; +using SFML.Window; +using System.Diagnostics; +using System.Security.Cryptography.X509Certificates; +using System.Security.Principal; + +namespace SFMLGame +{ + public class Programm + { + + public static Game game; + public static int WindowWidth, WindowHeight; + private static bool paused = false; + private static bool dmsg = true; + private static bool leftBtnDown; + + public static async Task Main() + { + WindowHeight = 1080; + WindowWidth = 1920; + byte[] fontarial = File.ReadAllBytes("fonts\\arial.ttf"); + Font font = new(fontarial); + RenderWindow window = new(new VideoMode((uint)WindowWidth, (uint)WindowHeight), "GameOfLife SFML"); + + window.Closed += new EventHandler(OnClosedEvent); + window.KeyPressed += new EventHandler(OnKeyPressedEvent); + window.MouseButtonPressed += new EventHandler(OnMouseDown); + window.MouseMoved += new EventHandler(OnMouseMoved); + window.MouseButtonReleased += new EventHandler(OnMouseReleased); + + Transform t = new(); + t.Scale(0.5f, 0.5f); + + RenderStates rs = new(); + rs.BlendMode = BlendMode.Add; + rs.Transform = t; + + + game = new(WindowHeight / 4, WindowWidth / 4); + game.SetRandomStart(WindowHeight, WindowWidth, mod: 4); + VertexArray vtx = new(PrimitiveType.Points); + int framecount = 0; + int CurrentFPS = 0; + window.SetFramerateLimit(120); + Stopwatch fpsWatch = new(); + fpsWatch.Start(); + int CellCount = 0; + int drawcalls; + List objects = []; + + + while (window.IsOpen) + { + Stopwatch watch = new(); + window.DispatchEvents(); + window.Clear(); + if (!paused) + { + watch.Start(); + game.Update(); + if (dmsg) + { + Console.WriteLine($"Update took {watch.ElapsedMilliseconds}ms"); + } + }; + + if (fpsWatch.ElapsedMilliseconds > 1000) + { + CurrentFPS = framecount; + framecount = 0; + fpsWatch.Restart(); + } + + Render(game.getActiveMap(), out drawcalls, ref window); + + + + //DrawAll(ref window, ref objects); + watch.Restart(); + window.Draw(GetFPSText(CurrentFPS, font)); + window.Draw(GetCellCountText(CellCount, font)); + window.Draw(GetIterationText(game.Iteration, font)); + + if (dmsg) + { + Console.WriteLine($"UI took {watch.ElapsedMilliseconds}ms | {watch.ElapsedTicks}ticks"); + } + + if (framecount % 5 == 0) + { + watch.Restart(); + CellCount = game.getActiveMap().GetAliveCells(); if (dmsg) + { + Console.WriteLine($"Cell Count took {watch.ElapsedMilliseconds}ms | {watch.ElapsedTicks}ticks"); + } + + } + + //window.Draw(vtx); + + + + + //if (dmsg) + //{ + // Console.WriteLine($"Drawing took {watch.ElapsedMilliseconds}ms"); + //} + + watch.Restart(); + + window.Display(); + + if (dmsg) + { + Console.WriteLine($"Display took {watch.ElapsedMilliseconds}ms | {watch.ElapsedTicks}ticks"); + } + + + + framecount++; + + watch.Stop(); + + } + + window.Dispose(); + + + + } + + private static void DrawAll(ref RenderWindow window, ref List objects) + { + foreach (var draw in objects) + { + window.Draw(draw); + } + } + + private static Drawable GetIterationText(int iteration, Font font) + { + Text ret = new Text($"Iterations: {iteration}", font); + ret.Position = new Vector2f(0, 20); + return ret; + } + + private static Drawable GetCellCountText(int value, Font font) + { + Text ret = new Text($"CellCOunt: {value}", font); + ret.Position = new Vector2f(0, 40); + return ret; + } + + private static void OnMouseMoved(object? sender, MouseMoveEventArgs e) + { + if (leftBtnDown) + { + game.getActiveMap().GetCell(e.Y / 4, e.X / 4).isAlive = true; + Console.WriteLine($"Edit Cell at {e.Y},{e.X}"); + } + } + + private static void OnMouseReleased(object? sender, MouseButtonEventArgs e) + { + if (e.Button == Mouse.Button.Left) + { + Console.WriteLine($"X: {e.X} Y: {e.Y}"); + leftBtnDown = false; + } + } + + private static void OnMouseDown(object? sender, MouseButtonEventArgs e) + { + if (e.Button == Mouse.Button.Left) + { + Console.WriteLine($"X: {e.X} Y: {e.Y}"); + game.getActiveMap().GetCell(e.Y / 4, e.X / 4).isAlive = true; + leftBtnDown = true; + } + //game.getActiveMap().GetCell(e.Y/4, e.X/4).isAlive = true; + } + + private static Text GetFPSText(int currentFPS, Font font) + { + + Text txt = new($"FPS: {currentFPS}", font); + return txt; + } + + private static void Render(in GameMap gameMap, ref VertexArray vtx) + { + vtx.Clear(); + + foreach (var cell in gameMap.GetAllCells()) + { + vtx.Append(new Vertex(new Vector2f(cell.YCoord * 4, cell.XCoord * 4), GetColor(cell))); + } + + } + + private static void Render(in GameMap gameMap, out int DrawCalls, ref RenderWindow window) + { + /** + drawables = [];//int ctn=0; + var sizeScalar = new Vector2f(4f, 4f); + RectangleShape shape = new(sizeScalar); + int drawCalls = 0; + foreach (var cell in gameMap.GetAllCells()) + { + + shape.Position = new Vector2f(cell.YCoord * 4, cell.XCoord * 4); + //shape.Scale = new(2f, 2f); + //shape.Radius = 1f; + //shape.Size = sizeScalar; + shape.FillColor = GetColor(cell); + shape.OutlineThickness = 1f; + shape.OutlineColor = Color.Black; + //window.Draw(shape); + window.Draw(shape); + drawCalls++; + //drawables.Add(shape); + //ctn++; + //vtx.Append(new Vertex(new Vector2f(cell.YCoord * 4, cell.XCoord * 4), GetColor(cell))); + }**/ + //shape.Dispose(); + + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); + var sizeScalar = new Vector2f(4f, 4f); + DrawCalls = 0; + VertexArray vtx = new VertexArray(PrimitiveType.Quads); + Color cellColor; + Vector2f position; + + object lockObj = new object(); + + + + foreach (var cell in gameMap.GetAllCells()) + { + position.X = cell.YCoord * 4; + position.Y = cell.XCoord * 4; + cellColor = GetColor(cell); + + // Create four vertices for each rectangle (since it's a quad) + vtx.Append(new Vertex(position, cellColor)); // Top-left + vtx.Append(new Vertex(new Vector2f(position.X + sizeScalar.X, position.Y), cellColor)); // Top-right + vtx.Append(new Vertex(new Vector2f(position.X + sizeScalar.X, position.Y + sizeScalar.Y), cellColor)); // Bottom-right + vtx.Append(new Vertex(new Vector2f(position.X, position.Y + sizeScalar.Y), cellColor)); // Bottom-left + + + } + window.Draw(vtx); + vtx.Dispose(); + if(dmsg) + { + Console.WriteLine($"Drawing took {stopwatch.ElapsedMilliseconds}ms | {stopwatch.ElapsedTicks}ticks"); + } + stopwatch.Stop(); + //Console.WriteLine($"Draw Calls per Frame: {drawCalls}"); + return; + } + + private static Color GetColor(CellState cell) + { + return cell.isAlive ? Color.White : Color.Black; + } + + public static void OnClosedEvent(object sender, EventArgs e) + { + ((Window)sender).Close(); + // ((Window)sender).Dispose(); + } + public static void OnKeyPressedEvent(object sender, KeyEventArgs e) + { + if (e.Code == Keyboard.Key.Escape) + { + ((Window)sender).Close(); + } + if (e.Code == Keyboard.Key.R) + { + game.SetRandomStart(WindowHeight, WindowWidth, mod: 4); + } + if (e.Code == Keyboard.Key.P) + { + paused = !paused; + } + if (e.Code == Keyboard.Key.D) + { + dmsg = !dmsg; + } + } + + public void InitWindow() + { + + } + + } +} \ No newline at end of file diff --git a/SFMLGame/SFMLGame.csproj b/SFMLGame/SFMLGame.csproj new file mode 100644 index 0000000..e1a0c4b --- /dev/null +++ b/SFMLGame/SFMLGame.csproj @@ -0,0 +1,24 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/SFMLGame/fonts/arial.TTF b/SFMLGame/fonts/arial.TTF new file mode 100644 index 0000000..27372d9 Binary files /dev/null and b/SFMLGame/fonts/arial.TTF differ