From 91e0b275c28364df8b1afe3a000cf2ba3d37663e Mon Sep 17 00:00:00 2001 From: EinEtw4s <74488187+EinEtw4s@users.noreply.github.com> Date: Fri, 16 Dec 2022 20:41:01 +0100 Subject: [PATCH 1/5] Create dotnet-desktop.yml --- .github/workflows/dotnet-desktop.yml | 115 +++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 .github/workflows/dotnet-desktop.yml diff --git a/.github/workflows/dotnet-desktop.yml b/.github/workflows/dotnet-desktop.yml new file mode 100644 index 0000000..42518fb --- /dev/null +++ b/.github/workflows/dotnet-desktop.yml @@ -0,0 +1,115 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# This workflow will build, test, sign and package a WPF or Windows Forms desktop application +# built on .NET Core. +# To learn how to migrate your existing application to .NET Core, +# refer to https://docs.microsoft.com/en-us/dotnet/desktop-wpf/migration/convert-project-from-net-framework +# +# To configure this workflow: +# +# 1. Configure environment variables +# GitHub sets default environment variables for every workflow run. +# Replace the variables relative to your project in the "env" section below. +# +# 2. Signing +# Generate a signing certificate in the Windows Application +# Packaging Project or add an existing signing certificate to the project. +# Next, use PowerShell to encode the .pfx file using Base64 encoding +# by running the following Powershell script to generate the output string: +# +# $pfx_cert = Get-Content '.\SigningCertificate.pfx' -Encoding Byte +# [System.Convert]::ToBase64String($pfx_cert) | Out-File 'SigningCertificate_Encoded.txt' +# +# Open the output file, SigningCertificate_Encoded.txt, and copy the +# string inside. Then, add the string to the repo as a GitHub secret +# and name it "Base64_Encoded_Pfx." +# For more information on how to configure your signing certificate for +# this workflow, refer to https://github.com/microsoft/github-actions-for-desktop-apps#signing +# +# Finally, add the signing certificate password to the repo as a secret and name it "Pfx_Key". +# See "Build the Windows Application Packaging project" below to see how the secret is used. +# +# For more information on GitHub Actions, refer to https://github.com/features/actions +# For a complete CI/CD sample to get started with GitHub Action workflows for Desktop Applications, +# refer to https://github.com/microsoft/github-actions-for-desktop-apps + +name: .NET Core Desktop + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + + build: + + strategy: + matrix: + configuration: [Debug, Release] + + runs-on: windows-latest # For a list of available runner types, refer to + # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on + + env: + Solution_Name: Game_of_Life.sln # Replace with your solution name, i.e. MyWpfApp.sln. + # Test_Project_Path: your-test-project-path # Replace with the path to your test project, i.e. MyWpfApp.Tests\MyWpfApp.Tests.csproj. + # Wap_Project_Directory: your-wap-project-directory-name # Replace with the Wap project directory relative to the solution, i.e. MyWpfApp.Package. + # Wap_Project_Path: your-wap-project-path # Replace with the path to your Wap project, i.e. MyWpf.App.Package\MyWpfApp.Package.wapproj. + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + # Install the .NET Core workload + - name: Install .NET Core + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 6.0.x + + # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild + - name: Setup MSBuild.exe + uses: microsoft/setup-msbuild@v1.0.2 + + # Execute all unit tests in the solution + - name: Execute unit tests + run: dotnet test + + # Restore the application to populate the obj folder with RuntimeIdentifiers + - name: Restore the application + run: msbuild $env:Solution_Name /t:Restore /p:Configuration=$env:Configuration + env: + Configuration: ${{ matrix.configuration }} + + # Decode the base 64 encoded pfx and save the Signing_Certificate + - name: Decode the pfx + run: | + $pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.Base64_Encoded_Pfx }}") + $certificatePath = Join-Path -Path $env:Wap_Project_Directory -ChildPath GitHubActionsWorkflow.pfx + [IO.File]::WriteAllBytes("$certificatePath", $pfx_cert_byte) + + # Create the app package by building and packaging the Windows Application Packaging project + - name: Create the app package + run: msbuild $env:Wap_Project_Path /p:Configuration=$env:Configuration /p:UapAppxPackageBuildMode=$env:Appx_Package_Build_Mode /p:AppxBundle=$env:Appx_Bundle /p:PackageCertificateKeyFile=GitHubActionsWorkflow.pfx /p:PackageCertificatePassword=${{ secrets.Pfx_Key }} + env: + Appx_Bundle: Always + Appx_Bundle_Platforms: x86|x64 + Appx_Package_Build_Mode: StoreUpload + Configuration: ${{ matrix.configuration }} + + # Remove the pfx + - name: Remove the pfx + run: Remove-Item -path $env:Wap_Project_Directory\GitHubActionsWorkflow.pfx + + # Upload the MSIX package: https://github.com/marketplace/actions/upload-a-build-artifact + - name: Upload build artifacts + uses: actions/upload-artifact@v3 + with: + name: MSIX Package + path: ${{ env.Wap_Project_Directory }}\AppPackages -- 2.45.2 From 58d4de3232fe9009e1be49e12caa09c084d1d096 Mon Sep 17 00:00:00 2001 From: EinEtw4s <74488187+EinEtw4s@users.noreply.github.com> Date: Fri, 16 Dec 2022 20:48:27 +0100 Subject: [PATCH 2/5] Update dotnet-desktop.yml --- .github/workflows/dotnet-desktop.yml | 121 ++++----------------------- 1 file changed, 15 insertions(+), 106 deletions(-) diff --git a/.github/workflows/dotnet-desktop.yml b/.github/workflows/dotnet-desktop.yml index 42518fb..164ac15 100644 --- a/.github/workflows/dotnet-desktop.yml +++ b/.github/workflows/dotnet-desktop.yml @@ -1,115 +1,24 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. +name: dotnet package -# This workflow will build, test, sign and package a WPF or Windows Forms desktop application -# built on .NET Core. -# To learn how to migrate your existing application to .NET Core, -# refer to https://docs.microsoft.com/en-us/dotnet/desktop-wpf/migration/convert-project-from-net-framework -# -# To configure this workflow: -# -# 1. Configure environment variables -# GitHub sets default environment variables for every workflow run. -# Replace the variables relative to your project in the "env" section below. -# -# 2. Signing -# Generate a signing certificate in the Windows Application -# Packaging Project or add an existing signing certificate to the project. -# Next, use PowerShell to encode the .pfx file using Base64 encoding -# by running the following Powershell script to generate the output string: -# -# $pfx_cert = Get-Content '.\SigningCertificate.pfx' -Encoding Byte -# [System.Convert]::ToBase64String($pfx_cert) | Out-File 'SigningCertificate_Encoded.txt' -# -# Open the output file, SigningCertificate_Encoded.txt, and copy the -# string inside. Then, add the string to the repo as a GitHub secret -# and name it "Base64_Encoded_Pfx." -# For more information on how to configure your signing certificate for -# this workflow, refer to https://github.com/microsoft/github-actions-for-desktop-apps#signing -# -# Finally, add the signing certificate password to the repo as a secret and name it "Pfx_Key". -# See "Build the Windows Application Packaging project" below to see how the secret is used. -# -# For more information on GitHub Actions, refer to https://github.com/features/actions -# For a complete CI/CD sample to get started with GitHub Action workflows for Desktop Applications, -# refer to https://github.com/microsoft/github-actions-for-desktop-apps - -name: .NET Core Desktop - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] +on: [push] jobs: - build: + runs-on: windows-latest strategy: matrix: - configuration: [Debug, Release] - - runs-on: windows-latest # For a list of available runner types, refer to - # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on - - env: - Solution_Name: Game_of_Life.sln # Replace with your solution name, i.e. MyWpfApp.sln. - # Test_Project_Path: your-test-project-path # Replace with the path to your test project, i.e. MyWpfApp.Tests\MyWpfApp.Tests.csproj. - # Wap_Project_Directory: your-wap-project-directory-name # Replace with the Wap project directory relative to the solution, i.e. MyWpfApp.Package. - # Wap_Project_Path: your-wap-project-path # Replace with the path to your Wap project, i.e. MyWpf.App.Package\MyWpfApp.Package.wapproj. + dotnet-version: ['7.0', '7.0.x' ] steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - # Install the .NET Core workload - - name: Install .NET Core - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 6.0.x - - # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild - - name: Setup MSBuild.exe - uses: microsoft/setup-msbuild@v1.0.2 - - # Execute all unit tests in the solution - - name: Execute unit tests - run: dotnet test - - # Restore the application to populate the obj folder with RuntimeIdentifiers - - name: Restore the application - run: msbuild $env:Solution_Name /t:Restore /p:Configuration=$env:Configuration - env: - Configuration: ${{ matrix.configuration }} - - # Decode the base 64 encoded pfx and save the Signing_Certificate - - name: Decode the pfx - run: | - $pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.Base64_Encoded_Pfx }}") - $certificatePath = Join-Path -Path $env:Wap_Project_Directory -ChildPath GitHubActionsWorkflow.pfx - [IO.File]::WriteAllBytes("$certificatePath", $pfx_cert_byte) - - # Create the app package by building and packaging the Windows Application Packaging project - - name: Create the app package - run: msbuild $env:Wap_Project_Path /p:Configuration=$env:Configuration /p:UapAppxPackageBuildMode=$env:Appx_Package_Build_Mode /p:AppxBundle=$env:Appx_Bundle /p:PackageCertificateKeyFile=GitHubActionsWorkflow.pfx /p:PackageCertificatePassword=${{ secrets.Pfx_Key }} - env: - Appx_Bundle: Always - Appx_Bundle_Platforms: x86|x64 - Appx_Package_Build_Mode: StoreUpload - Configuration: ${{ matrix.configuration }} - - # Remove the pfx - - name: Remove the pfx - run: Remove-Item -path $env:Wap_Project_Directory\GitHubActionsWorkflow.pfx - - # Upload the MSIX package: https://github.com/marketplace/actions/upload-a-build-artifact - - name: Upload build artifacts - uses: actions/upload-artifact@v3 - with: - name: MSIX Package - path: ${{ env.Wap_Project_Directory }}\AppPackages + - uses: actions/checkout@v3 + - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{ matrix.dotnet-version }} + - name: Install dependencies + run: dotnet restore + - name: Build + run: dotnet build --configuration Release --no-restore + - name: Test + run: dotnet test --no-restore --verbosity normal -- 2.45.2 From 88c26f29dc30943316388fa9d2b4341bcdc9706a Mon Sep 17 00:00:00 2001 From: EinEtw4s <74488187+EinEtw4s@users.noreply.github.com> Date: Fri, 16 Dec 2022 20:53:21 +0100 Subject: [PATCH 3/5] Update dotnet-desktop.yml --- .github/workflows/dotnet-desktop.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnet-desktop.yml b/.github/workflows/dotnet-desktop.yml index 164ac15..3c5f13f 100644 --- a/.github/workflows/dotnet-desktop.yml +++ b/.github/workflows/dotnet-desktop.yml @@ -8,7 +8,7 @@ jobs: runs-on: windows-latest strategy: matrix: - dotnet-version: ['7.0', '7.0.x' ] + dotnet-version: ['7.0.x' ] steps: - uses: actions/checkout@v3 @@ -20,5 +20,8 @@ jobs: run: dotnet restore - name: Build run: dotnet build --configuration Release --no-restore - - name: Test - run: dotnet test --no-restore --verbosity normal + - name: Archive build results + uses: actions/upload-artifact@v2 + with: + name: build-result + path: ./bin/ -- 2.45.2 From 70e6b8985a70d72021ce16981445d46c18efe849 Mon Sep 17 00:00:00 2001 From: EinEtw4s <74488187+EinEtw4s@users.noreply.github.com> Date: Fri, 16 Dec 2022 20:56:32 +0100 Subject: [PATCH 4/5] Update dotnet-desktop.yml --- .github/workflows/dotnet-desktop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet-desktop.yml b/.github/workflows/dotnet-desktop.yml index 3c5f13f..cbf2331 100644 --- a/.github/workflows/dotnet-desktop.yml +++ b/.github/workflows/dotnet-desktop.yml @@ -24,4 +24,4 @@ jobs: uses: actions/upload-artifact@v2 with: name: build-result - path: ./bin/ + path: Game_of_Life\bin\ -- 2.45.2 From 432ab66333bdfe237ad293fedd6cc789debcb050 Mon Sep 17 00:00:00 2001 From: Finn Erdmann Date: Tue, 10 Jan 2023 22:50:13 +0100 Subject: [PATCH 5/5] Lots of new and improved things; mainly a 10x (I'm bullshitting here) performance improvement and many other small fixes and nice things --- Game_of_Life/MainForm1.Designer.cs | 58 ++++- Game_of_Life/MainForm1.cs | 145 ++++++++++--- Game_of_Life/Pixelmap.cs | 337 ++++++++++++++++++++++------- 3 files changed, 435 insertions(+), 105 deletions(-) diff --git a/Game_of_Life/MainForm1.Designer.cs b/Game_of_Life/MainForm1.Designer.cs index 0b41902..7cc0e80 100644 --- a/Game_of_Life/MainForm1.Designer.cs +++ b/Game_of_Life/MainForm1.Designer.cs @@ -37,10 +37,14 @@ partial class MainForm1 this.buttonRepopulate = new System.Windows.Forms.Button(); this.zoomInButton = new System.Windows.Forms.Button(); this.zoomOutButton = new System.Windows.Forms.Button(); + this.buttonTickOnce = new System.Windows.Forms.Button(); this.buttonTick = new System.Windows.Forms.Button(); this.buttonClear = new System.Windows.Forms.Button(); + this.numericRenderSleep = new System.Windows.Forms.NumericUpDown(); + this.buttonGameArea = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.scalingSlider)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericRenderSleep)).BeginInit(); this.SuspendLayout(); // // pictureBox @@ -110,6 +114,17 @@ partial class MainForm1 this.zoomOutButton.UseVisualStyleBackColor = true; this.zoomOutButton.Click += new System.EventHandler(this.zoomOutButton_Click); // + // buttonTickOnce + // + this.buttonTickOnce.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonTickOnce.Location = new System.Drawing.Point(608, 41); + this.buttonTickOnce.Name = "buttonTickOnce"; + this.buttonTickOnce.Size = new System.Drawing.Size(87, 23); + this.buttonTickOnce.TabIndex = 6; + this.buttonTickOnce.Text = "TICK"; + this.buttonTickOnce.UseVisualStyleBackColor = true; + this.buttonTickOnce.Click += new System.EventHandler(this.buttonTickOnce_Click); + // // buttonTick // this.buttonTick.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); @@ -117,7 +132,7 @@ partial class MainForm1 this.buttonTick.Name = "buttonTick"; this.buttonTick.Size = new System.Drawing.Size(87, 23); this.buttonTick.TabIndex = 6; - this.buttonTick.Text = "TICK"; + this.buttonTick.Text = "⏯ PLAY"; this.buttonTick.UseVisualStyleBackColor = true; this.buttonTick.Click += new System.EventHandler(this.buttonTick_Click); // @@ -132,24 +147,60 @@ partial class MainForm1 this.buttonClear.UseVisualStyleBackColor = true; this.buttonClear.Click += new System.EventHandler(this.buttonClear_Click); // + // numericRenderSleep + // + this.numericRenderSleep.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.numericRenderSleep.DecimalPlaces = 1; + this.numericRenderSleep.Increment = new decimal(new int[] { + 167, + 0, + 0, + 65536}); + this.numericRenderSleep.Location = new System.Drawing.Point(512, 12); + this.numericRenderSleep.Maximum = new decimal(new int[] { + 10000, + 0, + 0, + 0}); + this.numericRenderSleep.Name = "numericRenderSleep"; + this.numericRenderSleep.Size = new System.Drawing.Size(87, 23); + this.numericRenderSleep.TabIndex = 9; + this.numericRenderSleep.UpDownAlign = System.Windows.Forms.LeftRightAlignment.Left; + this.numericRenderSleep.ValueChanged += new System.EventHandler(this.renderSleep_Changed); + // + // buttonGameArea + // + this.buttonGameArea.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonGameArea.Location = new System.Drawing.Point(512, 41); + this.buttonGameArea.Name = "buttonGameArea"; + this.buttonGameArea.Size = new System.Drawing.Size(87, 23); + this.buttonGameArea.TabIndex = 10; + this.buttonGameArea.Text = "SHOW AREA"; + this.buttonGameArea.UseVisualStyleBackColor = true; + this.buttonGameArea.Click += new System.EventHandler(this.buttonGameArea_Click); + // // MainForm1 // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.Black; this.ClientSize = new System.Drawing.Size(800, 450); + this.Controls.Add(this.buttonGameArea); + this.Controls.Add(this.numericRenderSleep); this.Controls.Add(this.buttonClear); this.Controls.Add(this.buttonTick); this.Controls.Add(this.zoomOutButton); this.Controls.Add(this.zoomInButton); this.Controls.Add(this.buttonRepopulate); + this.Controls.Add(this.buttonTickOnce); this.Controls.Add(this.scalingSlider); this.Controls.Add(this.debugLabel); this.Controls.Add(this.pictureBox); this.Name = "MainForm1"; - this.Text = "MainForm1"; + this.Text = "Game of Life :D"; ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.scalingSlider)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericRenderSleep)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); @@ -165,4 +216,7 @@ partial class MainForm1 private Button zoomOutButton; private Button buttonTick; private Button buttonClear; + private Button buttonTickOnce; + private NumericUpDown numericRenderSleep; + private Button buttonGameArea; } \ No newline at end of file diff --git a/Game_of_Life/MainForm1.cs b/Game_of_Life/MainForm1.cs index 12ada93..0199351 100644 --- a/Game_of_Life/MainForm1.cs +++ b/Game_of_Life/MainForm1.cs @@ -1,25 +1,26 @@ -using System.ComponentModel; +using System.Diagnostics; using System.Drawing.Drawing2D; -using System.Windows.Forms; -using System.Windows.Forms.VisualStyles; namespace Game_of_Life; public partial class MainForm1 : Form { private int scalingFactor = 10; - private int scalingAbsolute = 0; + private int scalingAbsolute; private int scalingMax; Pixelmap pixelmap; private int fieldHeight; private int fieldWidth; + private List renderThreads = new List(); + private decimal renderSleep = 0; + private bool showGameArea = false; public MainForm1() { InitializeComponent(); - fieldHeight = 1800; - fieldWidth = 3200; + fieldHeight = 160; + fieldWidth = 300; if (fieldWidth % 2 == 0) { scalingMax = fieldWidth - 2; @@ -34,15 +35,18 @@ public partial class MainForm1 : Form scalingSlider.Maximum = scalingMax; pixelmap = new Pixelmap(fieldHeight, fieldWidth, scalingFactor); + + /* Random rand = new Random(); - + for (int i = 0; i < 4000; i++) { - pixelmap.setPixel(rand.Next(fieldWidth), rand.Next(fieldHeight), rand.Next(3)); + pixelmap.SetPixel(rand.Next(fieldWidth), rand.Next(fieldHeight), rand.Next(3)); } + */ } - private void pictureBox1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) + private void pictureBox1_Paint(object sender, PaintEventArgs e) { int height = pictureBox.Size.Height; int width = pictureBox.Size.Width; @@ -51,7 +55,15 @@ public partial class MainForm1 : Form pictureBox.SizeMode = PictureBoxSizeMode.AutoSize; pictureBox.InterpolationMode = InterpolationMode.NearestNeighbor; - pixelmap.paintOnGraphics(graphics, width, height); + debugLabel.Text = $"{renderSleep}"; + if (!showGameArea) + { + pixelmap.paintOnGraphics(graphics, width, height); + } else + { + pixelmap.paintGameArea(graphics, width, height); + showGameArea = false; + } // Thread.Sleep(1000); } @@ -94,9 +106,9 @@ public partial class MainForm1 : Form } scalingSlider.Value = scalingAbsolute; - pixelmap.setScalingFactor(scalingFactor); - pixelmap.setScalingAbsolute(scalingAbsolute); - pictureBox.Refresh(); + pixelmap.SetScalingFactor(scalingFactor); + pixelmap.SetScalingAbsolute(scalingAbsolute); + pictureBox.Invalidate(); } private void scalingSlider_Changed(object sender, EventArgs e) @@ -107,26 +119,32 @@ public partial class MainForm1 : Form scalingAbsolute += 1; scalingSlider.Value = scalingAbsolute; } - pixelmap.setScalingAbsolute(scalingAbsolute); - pictureBox.Refresh(); + pixelmap.SetScalingAbsolute(scalingAbsolute); + pictureBox.Invalidate(); + } + + private void renderSleep_Changed(object sender, EventArgs e) + { + renderSleep = numericRenderSleep.Value; } private void buttonRepopulate_Click(object sender, EventArgs e) { - pixelmap.clear(); + pixelmap.Clear(); Random rand = new Random(); for (int i = 0; i < 4000; i++) { - pixelmap.setPixel(rand.Next(fieldWidth), rand.Next(fieldHeight), rand.Next(3)); + pixelmap.SetPixel(rand.Next(fieldWidth), rand.Next(fieldHeight), rand.Next(3)); } - pictureBox.Refresh(); + pictureBox.Invalidate(); } private void pictureBox_Click(object sender, EventArgs e) { - pixelmap.cyclePixel(MousePosition.X, MousePosition.Y); - pictureBox.Refresh(); + var cursorLocation = PointToClient(Cursor.Position); + pixelmap.CyclePixel(cursorLocation.X, cursorLocation.Y); + pictureBox.Invalidate(); } private void zoomInButton_Click(object sender, EventArgs e) @@ -152,7 +170,7 @@ public partial class MainForm1 : Form } scalingSlider.Value = scalingAbsolute; - pixelmap.setScalingAbsolute(scalingAbsolute); + pixelmap.SetScalingAbsolute(scalingAbsolute); } private void zoomOutButton_Click(object sender, EventArgs e) @@ -178,18 +196,95 @@ public partial class MainForm1 : Form } scalingSlider.Value = scalingAbsolute; - pixelmap.setScalingAbsolute(scalingAbsolute); + pixelmap.SetScalingAbsolute(scalingAbsolute); + } + + delegate void SetRefreshPictureBoxCallback(); + + private void RefreshPictureBox() + { + try + { + if (pictureBox.InvokeRequired) + { + SetRefreshPictureBoxCallback d = RefreshPictureBox; + Invoke(d, new object[] { }); + } + else + { + pictureBox.Invalidate(); + } + } + catch (ThreadInterruptedException) + { + throw new Exception("Thread interrupted"); + } catch(Exception e) + { + throw new Exception(""); + } + } + + private void RenderTick() + { + try + { + while (true) + { + var stopwatch = Stopwatch.StartNew(); + + pixelmap.Tick(); + RefreshPictureBox(); + + while ((decimal)stopwatch.Elapsed.Milliseconds <= renderSleep) + { + Thread.Sleep(1); + } + } + } + catch (Exception) + { + RefreshPictureBox(); + // ignored; can exit without much consequences + } } private void buttonTick_Click(object sender, EventArgs e) + { + if (renderThreads.Count == 0) + { + var rThread = new Thread(RenderTick); + rThread.Start(); + renderThreads.Add(rThread); + buttonTick.Text = "⏸PAUSE"; + } else + { + renderThreads.ForEach(r => { r.Interrupt(); }); + renderThreads.Clear(); + buttonTick.Text = "⏯PLAY"; + } + } + + private void buttonTickOnce_Click(object sender, EventArgs e) { pixelmap.Tick(); - pictureBox.Refresh(); + pictureBox.Invalidate(); } private void buttonClear_Click(object sender, EventArgs e) { - pixelmap.clear(); - pictureBox.Refresh(); + pixelmap.Clear(); + var mp = pixelmap.midPoint; + pixelmap.SetPixel(mp[0], mp[1], 1); + pixelmap.SetPixel((int)mp[0], (int)mp[1] - 1, 1); + pixelmap.SetPixel((int)mp[0], (int)mp[1] - 2, 1); + pixelmap.SetPixel((int)mp[0] - 1, (int)mp[1] - 1, 1); + pixelmap.SetPixel((int)mp[0] + 1, (int)mp[1] - 2, 1); + pictureBox.Invalidate(); + } + + private void buttonGameArea_Click(object sender, EventArgs e) + { + showGameArea = true; + pictureBox.Invalidate(); } } diff --git a/Game_of_Life/Pixelmap.cs b/Game_of_Life/Pixelmap.cs index 8a30fd8..7aca940 100644 --- a/Game_of_Life/Pixelmap.cs +++ b/Game_of_Life/Pixelmap.cs @@ -6,81 +6,82 @@ public class Pixelmap private int width; private int[,] map; private int scalingFactor; - private int[] midPoint; + public int[] midPoint; private int viewWidth; private int viewHeight; private int scalingAbsolute; private Color[] colors = {Color.Black, Color.White, Color.OrangeRed}; + // gameWindow = [leftBound, rightBound, upperBound, lowerBound] - private int[] gameWindow = {0, 0, 0, 0}; + public int[] gameWindow = { 0, 0, 0, 0 }; public Pixelmap(int height, int width, int scalingFactor) { this.height = height; this.width = width; map = new int[width, height]; - + gameWindow = new int[] { width, 0, height, 0 }; + this.scalingFactor = scalingFactor; this.midPoint = new int[] { width / 2, height / 2 }; this.scalingAbsolute = 0; } - public void setScalingFactor(int scalingFactor) + public void SetScalingFactor(int scalingFactor) { this.scalingFactor = scalingFactor; } - public void setScalingAbsolute(int scalingAbsolute) + public void SetScalingAbsolute(int scalingAbsolute) { this.scalingAbsolute = scalingAbsolute; } - public void clear() + public void Clear() { map = new int[width, height]; - gameWindow = new int[] {0, 0, 0, 0}; + gameWindow = new int[] { width, 0, height, 0 }; } - public void setPixel(int x, int y, int val) + private void SetBounds(int x, int y) + { + x = x == 0 ? 2 : x; + y = y == 0 ? 2 : y; + + x = x == width ? width - 2 : x; + y = y == height ? height - 2 : y; + + if (x < gameWindow[0]) + { + gameWindow[0] = x; + } + + if (x > gameWindow[1]) + { + gameWindow[1] = x; + } + + if (y < gameWindow[2]) + { + gameWindow[2] = y; + } + + if (y > gameWindow[3]) + { + gameWindow[3] = y; + } + } + + public void SetPixel(int x, int y, int val) { val = val >= 3 ? 0 : val; map[x, y] = val; - if (x == 0) - { - x = 1; - } else if (x == width) - { - x--; - } - - if (y == 0) - { - y = 1; - } else if (y == height) - { - y--; - } - - if (x < midPoint[0] - gameWindow[0]) - { - gameWindow[0] = midPoint[0]-x; - } - if (x > midPoint[0] + gameWindow[1]) - { - gameWindow[1] = midPoint[0] - x; - } - if (y < midPoint[1] - gameWindow[2]) - { - gameWindow[2] = midPoint[1] - y; - } - if (y > midPoint[1] + gameWindow[3]) - { - gameWindow[3] = midPoint[1] - y; - } + SetBounds(x, y); } - public void cyclePixel(int x, int y) + + public void CyclePixel(int x, int y) { float aspectRatio = (float)viewHeight / (float)viewWidth; @@ -105,35 +106,35 @@ public class Pixelmap upperBound = 0; } - float xTrans = (float)(x-scaleWidth) / scaleWidth; - float yTrans = (float)(y-scaleHeight) / scaleHeight; + float xTrans = (float)(x- scaleWidth - 0.5 * scaleWidth) / scaleWidth; + float yTrans = (float)(y- scaleHeight + 0.5 * scaleHeight) / scaleHeight; int pixelX = (int)Math.Ceiling(xTrans + (float)leftBound); int pixelY = (int)Math.Ceiling(yTrans + (float)upperBound); int val = map[pixelX, pixelY] + 1; - setPixel(pixelX, pixelY, val); + SetPixel(pixelX, pixelY, val); } - public Graphics paintOnGraphics(Graphics g, int viewWidth, int viewHeight) + public Graphics paintOnGraphics(Graphics g, int viewportWidth, int viewportHeight) { g.Clear(Color.Black); Color penColor = Color.FromArgb(125, Color.Beige); Pen linePen = new Pen(penColor, 1); - this.viewHeight = viewHeight; - this.viewWidth = viewWidth; + viewHeight = viewportHeight; + viewWidth = viewportWidth; - int[] viewMid = { viewWidth / 2, viewHeight / 2 }; + int[] viewMid = { viewportWidth / 2, viewportHeight / 2 }; - float aspectRatio = (float)viewHeight / (float)viewWidth; + float aspectRatio = viewportHeight / (float)viewportWidth; // set width and needed height of the border of the viewable area int borderWidth = width - scalingAbsolute; - int borderHeight = (int)Math.Ceiling((float)borderWidth * aspectRatio); + int borderHeight = (int)Math.Ceiling(borderWidth * aspectRatio); // scaling factors for both dimensions - float scaleWidth = (float)viewWidth / (float)borderWidth; - float scaleHeight = (float)viewHeight / (float)borderHeight; + float scaleWidth = viewportWidth / (float)borderWidth; + float scaleHeight = viewportHeight / (float)borderHeight; // ensure that pixel width and height are always at least 1px int scalePixelWidth; @@ -157,24 +158,24 @@ public class Pixelmap } // set both left and right bound for the area being viewed - int leftBound = (int)Math.Ceiling(midPoint[0] - ((float)borderWidth / (float)2)); + int leftBound = (int)Math.Ceiling(midPoint[0] - (borderWidth / (float)2)); if (leftBound < 0) { leftBound = 0; } - int rightBound = (int)Math.Ceiling(midPoint[0] + ((float)borderWidth / (float)2)); + int rightBound = (int)Math.Ceiling(midPoint[0] + (borderWidth / (float)2)); if (rightBound > width) { rightBound = width; } // set both upper and lower bound for the area being viewed - int upperBound = (int)Math.Ceiling(midPoint[1] - ((float)borderHeight / (float)2)); - if (upperBound < 0) + int upperBound = (int)Math.Ceiling(midPoint[1] - (borderHeight / (float)2)); + if (upperBound < 1) { - upperBound = 0; + upperBound = 1; } - int lowerBound = (int)Math.Ceiling(midPoint[1] + ((float)borderHeight / (float)2)); + int lowerBound = (int)Math.Ceiling(midPoint[1] + (borderHeight / (float)2)); if (lowerBound > height) { lowerBound = height; @@ -182,7 +183,7 @@ public class Pixelmap for (int w = leftBound; w < rightBound; w++) { - for (int h = upperBound; h < lowerBound; h++) + for (int h = (upperBound - 1); h < lowerBound; h++) { int deltaW = w - midPoint[0]; int deltaH = h - midPoint[1]; @@ -190,10 +191,10 @@ public class Pixelmap Color pixelColor = colors[map[w, h]]; if (!pixelColor.Equals(Color.Black)) { - int x = (int)(viewMid[0] + (deltaW * scaleWidth)); - int y = (int)(viewMid[1] + (deltaH * scaleHeight)); - int pixelWidth = (int)scalePixelWidth; - int pixelHeight = (int)scalePixelHeight; + int x = (int)(viewMid[0] + (deltaW * scaleWidth) + 0.5 * scaleWidth); + int y = (int)(viewMid[1] + (deltaH * scaleHeight) + 0.5 * scaleHeight); + int pixelWidth = scalePixelWidth; + int pixelHeight = scalePixelHeight; if (x < 0) { @@ -201,9 +202,9 @@ public class Pixelmap x = 0; pixelWidth = scalePixelWidth - deltaX; } - else if (x + (int)scalePixelWidth > viewWidth) + else if (x + scalePixelWidth > viewportWidth) { - int deltaX = viewWidth - x; + int deltaX = viewportWidth - x; pixelWidth = deltaX; } @@ -213,15 +214,141 @@ public class Pixelmap y = 0; pixelHeight = scalePixelHeight - deltaY; } - else if (y + (int)scalePixelHeight > viewHeight) + else if (y + scalePixelHeight > viewportHeight) { - int deltaY = viewHeight - y; + int deltaY = viewportHeight - y; pixelWidth = deltaY; } Brush pixelBrush = new SolidBrush(pixelColor); g.FillRectangle(pixelBrush, - new Rectangle(x, (int)(viewMid[1] + (deltaH * scaleHeight)), (int)scalePixelWidth, (int)scalePixelHeight)); + new Rectangle(x, (int)(viewMid[1] + (deltaH * scaleHeight)), scalePixelWidth, scalePixelHeight)); + } + } + } + return g; + } + + public Graphics paintGameArea(Graphics g, int viewportWidth, int viewportHeight) + { + g.Clear(Color.Black); + + Color penColor = Color.FromArgb(125, Color.Beige); + Pen linePen = new Pen(penColor, 1); + + viewHeight = viewportHeight; + viewWidth = viewportWidth; + + int[] viewMid = { viewportWidth / 2, viewportHeight / 2 }; + + float aspectRatio = viewportHeight / (float)viewportWidth; + + // set width and needed height of the border of the viewable area + int borderWidth = width - scalingAbsolute; + int borderHeight = (int)Math.Ceiling(borderWidth * aspectRatio); + + // scaling factors for both dimensions + float scaleWidth = viewportWidth / (float)borderWidth; + float scaleHeight = viewportHeight / (float)borderHeight; + + // ensure that pixel width and height are always at least 1px + int scalePixelWidth; + if (scaleWidth < 1) + { + scalePixelWidth = 1; + } + else + { + scalePixelWidth = (int)Math.Ceiling(scaleWidth); + } + + int scalePixelHeight; + if (scaleHeight < 1) + { + scalePixelHeight = 1; + } + else + { + scalePixelHeight = (int)Math.Ceiling(scaleHeight); + } + + // set both left and right bound for the area being viewed + int leftBound = (int)Math.Ceiling(midPoint[0] - (borderWidth / (float)2)); + if (leftBound < 0) + { + leftBound = 0; + } + int rightBound = (int)Math.Ceiling(midPoint[0] + (borderWidth / (float)2)); + if (rightBound > width) + { + rightBound = width; + } + + // set both upper and lower bound for the area being viewed + int upperBound = (int)Math.Ceiling(midPoint[1] - (borderHeight / (float)2)); + if (upperBound < 1) + { + upperBound = 1; + } + int lowerBound = (int)Math.Ceiling(midPoint[1] + (borderHeight / (float)2)); + if (lowerBound > height) + { + lowerBound = height; + } + + int[,] gameMap = new int[width, height]; + + for (int w2 = gameWindow[0] - 1; w2 <= gameWindow[1] + 1; w2++) + //for (int w = 1; w < width - 1; w++) + { + for (int h2 = gameWindow[2] - 1; h2 <= gameWindow[3] + 1; h2++) + { + gameMap[w2, h2] = 2; + } + } + + for (int w = leftBound; w < rightBound; w++) + { + for (int h = (upperBound - 1); h < lowerBound; h++) + { + int deltaW = w - midPoint[0]; + int deltaH = h - midPoint[1]; + + Color pixelColor = colors[gameMap[w,h]]; + if (!pixelColor.Equals(Color.Black)) + { + int x = (int)(viewMid[0] + (deltaW * scaleWidth) + 0.5 * scaleWidth); + int y = (int)(viewMid[1] + (deltaH * scaleHeight) + 0.5 * scaleHeight); + int pixelWidth = scalePixelWidth; + int pixelHeight = scalePixelHeight; + + if (x < 0) + { + int deltaX = 0 - x; + x = 0; + pixelWidth = scalePixelWidth - deltaX; + } + else if (x + scalePixelWidth > viewportWidth) + { + int deltaX = viewportWidth - x; + pixelWidth = deltaX; + } + + if (y < 0) + { + int deltaY = 0 - y; + y = 0; + pixelHeight = scalePixelHeight - deltaY; + } + else if (y + scalePixelHeight > viewportHeight) + { + int deltaY = viewportHeight - y; + pixelWidth = deltaY; + } + + Brush pixelBrush = new SolidBrush(pixelColor); + g.FillRectangle(pixelBrush, + new Rectangle(x, (int)(viewMid[1] + (deltaH * scaleHeight)), scalePixelWidth, scalePixelHeight)); } } } @@ -230,32 +357,85 @@ public class Pixelmap public void Tick() { - int[,] mapBuffer = new int[width, height]; + int[,]? mapBuffer = new int[width, height]; - // for (int w = midPoint[0]-gameWindow[0]; w < midPoint[0] + gameWindow[1]; w++) - for (int w = 1; w < width - 1; w++) + for (int w = gameWindow[0] - 2; w < gameWindow[1] + 2; w++) { - // for (int h = midPoint[1] - gameWindow[2]; h < midPoint[1] + gameWindow[3]; h++) - for (int h = 1; h < height - 1; h++) + for (int h = gameWindow[2] - 2; h < gameWindow[3] + 2; h++) { + if (w < 0) + { + continue; + } + else if (w >= width) + { + continue; + } + + if (h < 0) + { + continue; + } + else if (h >= height) + { + continue; + } + int neighbors = 0; + var rand = new Random(); + int numOptions = colors.Length; // iterate through all neighbors - for (int x = 0; x < 3; x++) + for (int x = -1; x < 2; x++) { - for (int y = 0; y < 3; y++) + for (int y = -1; y < 2; y++) { - if (map[(w - 1) + x, (h - 1) + y] != 0) + int mx = x + w; + int my = y + h; + if (mx < 0) + { + break; + } else if (mx >= width) + { + break; + } + + if (my < 0) + { + break; + } else if (my >= height) + { + break; + } + + int cell = 0; + + try + { + cell = map[x + w, y + h]; + } catch (Exception e) + { + break; + } + if (cell > 0) { neighbors += 1; } } } + int currCell = 0; + + try + { + currCell = map[w, h]; + } catch (Exception e) { continue; } + if (map[w, h] == 0) { if (neighbors == 3) { + SetBounds(w, h); mapBuffer[w, h] = 1; } } @@ -274,11 +454,13 @@ public class Pixelmap // lives case 2: mapBuffer[w, h] = 1; + SetBounds(w, h); break; // lives case 3: mapBuffer[w, h] = 1; + SetBounds(w, h); break; // dies @@ -291,7 +473,6 @@ public class Pixelmap } map = mapBuffer; - - return; + mapBuffer = null; } } \ No newline at end of file -- 2.45.2