From 51bacf6060d170677a299791cfe89e538ff1a69e Mon Sep 17 00:00:00 2001
From: Floris <f.k.h.vandezande@students.uu.nl>
Date: Sun, 1 Dec 2024 22:18:43 +0100
Subject: [PATCH] puzzleform update

- Fixed flickering when redrawing board by using BufferedGraphics
- Board is no longer marked as an optional parameter in PuzzleForm constructor
- UI appearance/layout changes
- GetTile method that returns board coordinates that touch a certain point (no longer static)
- Key/Mouse presses made will activate certain functions, including inputs to boardState (only if mouse touching board)
- Added input-handling abstract methods TileInput and TileClick to Board class

/ still missing solution for board not updating visually when boardState changes
/ solved 2 bugs by maximizing PuzzleForm window on start, removing this will make the bugs return
/ there is still at least one scenario where the input detection dies
---
 PuzzlePlayer/Binary.cs     |   6 +-
 PuzzlePlayer/Board.cs      |   7 ++
 PuzzlePlayer/PuzzleForm.cs | 138 ++++++++++++++++++++++++++++---------
 3 files changed, 119 insertions(+), 32 deletions(-)

diff --git a/PuzzlePlayer/Binary.cs b/PuzzlePlayer/Binary.cs
index 12aa1f2..c4127af 100644
--- a/PuzzlePlayer/Binary.cs
+++ b/PuzzlePlayer/Binary.cs
@@ -21,7 +21,7 @@ namespace PuzzlePlayer_Namespace
             // create a board with the specifide size
             boardState = new int[boardSize,boardSize];
 
-            description = "Binary puzzle is played on any even-numbered square grid, with some cells initially containing black or white circles. The goal of the puzzle is to fill all cells such that:\r\n- More than two circles of the same color cannot be adjacent\r\n- Each row and column must contain an equal number of black and white circles\r\n- Each row and column cannot appear multiple times on the board";
+            description = "Binary puzzle is played on any even-numbered square grid, with some cells initially containing black or white circles. The goal of the puzzle is to fill all cells such that:\r\n● More than two circles of the same color cannot be adjacent\r\n● Each row and column must contain an equal number of black and white circles\r\n● Each row and column cannot appear multiple times on the board";
 
             // clear the board (fill it in with -1)
             Clear();
@@ -202,5 +202,9 @@ namespace PuzzlePlayer_Namespace
             return false;
         }
 
+        public override void TileInput(Point p, int x)
+        {
+            if (x==0 || x==1) boardState[p.X, p.Y] = x;
+        }
     }
 }
\ No newline at end of file
diff --git a/PuzzlePlayer/Board.cs b/PuzzlePlayer/Board.cs
index d373062..01ec0d4 100644
--- a/PuzzlePlayer/Board.cs
+++ b/PuzzlePlayer/Board.cs
@@ -95,5 +95,12 @@ namespace PuzzlePlayer_Namespace
 
         // abstract methode for checking if a imputed boardstate is valid
         public abstract bool IsBoardValid(int[,] boardToCheck);
+
+        // changes tile P to value X
+        public abstract void TileInput(Point p, int x);
+
+        // performs a left/right (X) click on tile P, changing its value
+        public virtual void TileClick(Point p, int x) { }
+
     }
 }
diff --git a/PuzzlePlayer/PuzzleForm.cs b/PuzzlePlayer/PuzzleForm.cs
index 8b3aaf7..6c7b6ad 100644
--- a/PuzzlePlayer/PuzzleForm.cs
+++ b/PuzzlePlayer/PuzzleForm.cs
@@ -1,4 +1,5 @@
 using System;
+using System.CodeDom;
 using System.Collections.Generic;
 using System.Drawing;
 using System.Drawing.Text;
@@ -11,15 +12,15 @@ namespace PuzzlePlayer_Namespace
 {
     internal class PuzzleForm : Form
     {
-        private Button solvebutton;
-        private Button hintbutton;
-        private Button generatebutton;
-        private TextBox informationbox;
-        private Graphics graphics;
+        private readonly Button solvebutton;
+        private readonly Button hintbutton;
+        private readonly Button generatebutton;
+        private readonly Label titlebox;
+        private readonly Label informationbox;
         private Rectangle boardspace;
-
+        private readonly BufferedGraphics bufferedGraphics;
         private Board board;
-        public Board Board //Updating the Board member will immediately call board.Draw method so that the board is updated visually
+        public Board Board //updating the Board member will immediately call board.Draw method so that the board is updated visually
         {
             get { return board; }
             set
@@ -29,27 +30,48 @@ namespace PuzzlePlayer_Namespace
             }
         }
 
-        public PuzzleForm(Board b = null, Size s = default(Size)) //Takes Board and Size parameter and sets up the PuzzleForm window.
+        private readonly Button UPDATEBUTTON;
+        public PuzzleForm(Board b, Size s = default) //takes Board and Size parameter and sets up the PuzzleForm window.
         {
-            if (s == default(Size)) s = new Size(700, 420);
+            if (s == default) s = new Size(700, 420);
             this.Size = this.MinimumSize = s;
             this.WindowState = FormWindowState.Maximized;
             this.BackColor = Color.DarkGreen;
-            this.Text = "top text";
-            graphics = this.CreateGraphics();
-            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
+            this.Text = "PuzzlePlayer";
+            bufferedGraphics = BufferedGraphicsManager.Current.Allocate(this.CreateGraphics(), this.DisplayRectangle);
+            bufferedGraphics.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
             generatebutton = new Button();
             hintbutton = new Button();
             solvebutton = new Button();
-            informationbox = new TextBox();
+            UPDATEBUTTON = new Button();
+            titlebox = new Label();
+            informationbox = new Label();
             boardspace = new Rectangle(220, 30, 400, 400);
+
             this.Paint += (object o, PaintEventArgs pea) =>
             {
-                board.Draw(graphics, boardspace);
-                //graphics.FillRectangle(Brushes.LightCoral, 220, 30, this.Width - 380, this.Height - 100);
-                //graphics.FillRectangle(Brushes.DarkRed, boardspace);
+                board.Draw(bufferedGraphics.Graphics, boardspace);
+                //bufferedGraphics.Graphics.FillRectangle(Brushes.LightCoral, 220, 30, this.Width - 350, this.Height - 100);
+                //bufferedGraphics.Graphics.FillRectangle(Brushes.DarkRed, boardspace);
+                bufferedGraphics.Render();
             };
             this.Resize += (object o, EventArgs ea) => UpdateUI();
+            this.Move += (object o, EventArgs ea) => this.Focus();
+            this.KeyPress += (object o, KeyPressEventArgs kea) => Input(kea.KeyChar);
+            this.MouseClick += (object o, MouseEventArgs mea) =>
+            {
+                if (mea.Button == MouseButtons.Left)
+                {
+                    Input('[');
+                    return;
+                }
+                if (mea.Button == MouseButtons.Right)
+                {
+                    Input(']');
+                    return;
+                }
+            };
+
             Board = b;
             CreateUI();
             Board.boardState[1, 1] = 1;
@@ -61,58 +83,112 @@ namespace PuzzlePlayer_Namespace
             {
                 Controls.Add(b);
                 b.Size = new Size(80, 50);
-                b.BackColor = Color.Gainsboro;
+                b.FlatAppearance.BorderColor = b.BackColor = Color.Gainsboro;
                 b.Text = "DEFAULT0";
+                b.Font = new Font("Verdana", 12, FontStyle.Bold | FontStyle.Italic);
+                b.FlatStyle = FlatStyle.Flat;
+                b.GotFocus += (object o, EventArgs ea) => this.Focus();
             }
             CreateButton(generatebutton);
-            generatebutton.Text = "Generate";
+            generatebutton.Text = "New Game";
             generatebutton.MouseClick += (object sender, MouseEventArgs mea) => 
             {
-                //board = Board.Generate();
+                Board.Generate();
+                Board = Board;
             };
             CreateButton(hintbutton);
             hintbutton.Text = "Hint";
             hintbutton.MouseClick += (object sender, MouseEventArgs mea) =>
             {
                 MessageBox.Show("Hint: geef op");
+                Board = Board;
             };
             CreateButton(solvebutton);
             solvebutton.Text = "Solve";
             solvebutton.MouseClick += (object sender, MouseEventArgs mea) =>
             {
-                //board = board.Solve();
+                Board.Solve();
+                Board = Board;
+            };
+
+            CreateButton(UPDATEBUTTON);
+            UPDATEBUTTON.Text = "Update";
+            UPDATEBUTTON.Font = new Font("Verdana", 11, FontStyle.Bold);
+            UPDATEBUTTON.FlatAppearance.BorderColor = UPDATEBUTTON.BackColor = Color.Pink;
+            UPDATEBUTTON.MouseClick += (object sender, MouseEventArgs mea) =>
+            {
                 Board = Board;
             };
 
+            Controls.Add(titlebox);
+            titlebox.Location = new Point(20, 15);
+            titlebox.Size = new Size(180, 50);
+            titlebox.BackColor = this.BackColor;
+            titlebox.ForeColor = Color.White;
+            titlebox.Text = "Binary";
+            titlebox.Font = new Font("Verdana", 24, FontStyle.Bold);
+
             Controls.Add(informationbox);
-            informationbox.Multiline = true;
-            informationbox.Size = new Size(160, 300);
-            informationbox.BackColor = Color.LightGray;
+            informationbox.Location = new Point(20, 65);
+            informationbox.Size = new Size(180, 300);
+            informationbox.BackColor = this.BackColor;
+            informationbox.ForeColor = Color.White;
             informationbox.Text = board.description;
             informationbox.Font = new Font("Verdana", 10);
             UpdateUI();
         }
         private void UpdateUI() //resizes the boardspace rectangle and updates the rest of the ui around the new boardspace size
         {
-            graphics.Clear(this.BackColor);
-            boardspace.Size = FitBoard(new Size(Board.boardState.GetLength(0),Board.boardState.GetLength(1)),new Size(this.Width - 380, this.Height - 100),8);
+            bufferedGraphics.Graphics.Clear(this.BackColor);
+            boardspace.Size = FitBoard(new Size(Board.boardState.GetLength(0),Board.boardState.GetLength(1)),new Size(this.Width - 350, this.Height - 100),8);
             generatebutton.Location = new Point(boardspace.Right + 30, 30);
-            hintbutton.Location = new Point(boardspace.Right + 30, 130);
-            solvebutton.Location = new Point(boardspace.Right + 30, 230);
-            informationbox.Location = new Point(40, 30);
+            hintbutton.Location = new Point(boardspace.Right + 30, 110);
+            solvebutton.Location = new Point(boardspace.Right + 30, 190);
+            UPDATEBUTTON.Location = new Point(boardspace.Right + 30, 270);
             this.Invalidate();
         }
         public static Size FitBoard(Size gamesize, Size maxboardsize, int drawfactor) //returns the largest rectangle smaller than MaxBoardSize that fits the given GameSize well
         {
             int TileLength = Math.Min(maxboardsize.Width / gamesize.Width, maxboardsize.Height / gamesize.Height) / drawfactor * drawfactor;
             return new Size(gamesize.Width*TileLength, gamesize.Height*TileLength);
-            //After each of the divisions in this method, the value should be rounded down,
+            //after each of the divisions in this method, the value should be rounded down, 
             //as the end value cannot exceed the size given by maxboardsize.
             //however, C# rounds divisions of Int values down by default, so this does not have to be explicitly done.
         }
-        public static Point GetTile(Point p)
+        private Point GetTile(Size gamesize, Rectangle r, Point p) //returns board coordinate at mouse position P of a Gamesize board drawn in R
+        {
+            Point res = this.PointToClient(p) - (Size)r.Location;
+            res.X /= (r.Width / gamesize.Width);
+            res.Y /= (r.Height / gamesize.Height);
+            return res;
+        }
+        public void Input(char c) //checks if a command binded to the keyboard key and runs it, affects tile that is hovered on if applicable
         {
-            return p;
+            if (!"nhs[]1234567890".Contains(c)) return;
+            switch (c)
+            {
+                case 'n':
+                    return;
+                case 'h':
+                    MessageBox.Show("Hint: geef op");
+                    return;
+                case 's':
+                    return;
+
+            }
+            Point tile = GetTile(new Size(Board.boardState.GetLength(0), Board.boardState.GetLength(1)), boardspace, Control.MousePosition);
+            if (!(tile.X >= 0 && tile.X < Board.boardState.GetLength(0) && tile.Y >= 0 && tile.Y < Board.boardState.GetLength(1))) return;
+            if (c == '[' || c == ']') // '[' = 91, ']' = 93
+            {
+                //Board.TileClick(tile, (c - 91) / 2);
+                return;
+            }
+            if (char.GetNumericValue(c) != -1)
+            {
+                Board.TileInput(tile, (int)char.GetNumericValue(c));
+                return;
+            }
+            MessageBox.Show("uhoh");
         }
     }
 }
-- 
GitLab