diff --git a/PuzzlePlayer/Binary.cs b/PuzzlePlayer/Binary.cs index cd9d37e1c739a353f3cb9364a1b52e5a90f3ab9f..41fc11ef21bf3f7cb57d7c231b788aea7ec4f47c 100644 --- a/PuzzlePlayer/Binary.cs +++ b/PuzzlePlayer/Binary.cs @@ -22,37 +22,48 @@ namespace PuzzlePlayer_Namespace { boardState = GetClearBoard(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(); } - public override void Draw (Graphics gr, Point p, Size s) + public override void Draw (Graphics gr, Rectangle r) //draws board in rectangle R. warning: will stretch image unless FitBoard() is used for rectangle size { - gr.FillRectangle(Brushes.Beige, p.X, p.Y, s.Width, s.Height); + Size tilesize = new Size(r.Width / boardState.GetLength(0), r.Height / boardState.GetLength(1)); + Pen border = new Pen(Color.Black, 2); + gr.FillRectangle(Brushes.Beige, r.X, r.Y, tilesize.Width*boardState.GetLength(0), tilesize.Height*boardState.GetLength(1)); for (int i = 0; i < boardState.GetLength(0); i++) { for(int j = 0; j < boardState.GetLength(1); j++) { - gr.DrawRectangle(Pens.Black, - p.X+i*s.Width/ boardState.GetLength(0), - p.Y+j*s.Height/boardState.GetLength(1), - s.Width / boardState.GetLength(0), - s.Height / boardState.GetLength(1)); + + gr.DrawRectangle(Pens.LightGray, + r.X+i* tilesize.Width, + r.Y+j* tilesize.Height, + tilesize.Width, + tilesize.Height); if (boardState[i,j] == 0) { gr.FillEllipse(Brushes.White, - (int)(p.X + ((double)i + 0.125) * s.Width / boardState.GetLength(0)), - (int)(p.Y + ((double)j + 0.125) * s.Height / boardState.GetLength(1)), - s.Width / boardState.GetLength(0) * 3 / 4, - s.Height / boardState.GetLength(1) * 3 / 4); + (int)(r.X + ((double)i + 0.125) * tilesize.Width), + (int)(r.Y + ((double)j + 0.125) * tilesize.Height), + tilesize.Width * 3 / 4, + tilesize.Height * 3 / 4); + gr.DrawEllipse(border, + (int)(r.X + ((double)i + 0.125) * tilesize.Width + border.Width/2), + (int)(r.Y + ((double)j + 0.125) * tilesize.Height + border.Width/2), + tilesize.Width * 3 / 4 - border.Width, + tilesize.Height * 3 / 4 - border.Width); } if (boardState[i, j] == 1) { gr.FillEllipse(Brushes.Black, - (int)(p.X + ((double)i + 0.125) * s.Width / boardState.GetLength(0)), - (int)(p.Y + ((double)j + 0.125) * s.Height / boardState.GetLength(1)), - s.Width / boardState.GetLength(0) * 3 / 4, - s.Height / boardState.GetLength(1) * 3 / 4); + (int)(r.X + ((double)i + 0.125) * tilesize.Width), + (int)(r.Y + ((double)j + 0.125) * tilesize.Height), + tilesize.Width * 3 / 4, + tilesize.Height * 3 / 4); } } } @@ -438,6 +449,10 @@ namespace PuzzlePlayer_Namespace return true; 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 edc10d88bde424a171cf0bbfe6175c66cde37761..739b48af62f14fe0bedc6700cb7fe3387286292b 100644 --- a/PuzzlePlayer/Board.cs +++ b/PuzzlePlayer/Board.cs @@ -44,8 +44,22 @@ namespace PuzzlePlayer_Namespace public int[,] boardState; public int[,] lastGeneratedBoard; - public abstract void Draw(Graphics gr, Point p, Size s); + + // checks if the board is valid and solvable before setting the variable. + public bool setBoardState(int[,] newState) + { + int[,] copy = boardState; + boardState = newState; + if (IsBoardValid(newState) && Solve() == SOLUTIONS.UNIQUE) + return true; + else + { + boardState = copy; + return false; + } + } + public abstract void Draw(Graphics gr, Rectangle r); // a methode for solving the whole board. It uses the private SolveStep methode untill the whole board is solved // it has one parameter. setting this to true will only give a return value without changing the current boardState public SOLUTIONS Solve(bool CheckOnly) @@ -99,5 +113,14 @@ namespace PuzzlePlayer_Namespace // abstract methode for generating a random board public abstract void Generate(); + + // 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 6a2d6c680e372e9c8a5549f31ec4b54a9da0b5cb..dde1ea08cf464db52f64c8b1ce59135635bbd6de 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,67 +12,73 @@ namespace PuzzlePlayer_Namespace { internal class PuzzleForm : Form { - private Button solvebutton; - private Button hintbutton; - private Button generatebutton; - private Label boardlabel; - private TextBox informationbox; - public 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 { board = value; - board.Draw(graphics, new Point(220, 30), new Size(400, 400)); + this.Invalidate(); } } - 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(800, 500); - this.Size = s; - graphics = this.CreateGraphics(); + if (s == default) s = new Size(700, 420); + this.Size = this.MinimumSize = s; + this.WindowState = FormWindowState.Maximized; + this.BackColor = Color.DarkGreen; + 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(); - Board = b; - CreateUI(); - UpdateUI(); - this.Resize += (object sender, EventArgs ea) => + UPDATEBUTTON = new Button(); + titlebox = new Label(); + informationbox = new Label(); + boardspace = new Rectangle(220, 30, 400, 400); + + this.Paint += (object o, PaintEventArgs pea) => { - UpdateUI(); + 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; + } }; - // example board: https://imgur.com/spyYaPl - /* - int[,] test = Binary.GetClearBoard(Board.boardState.GetLength(0)); - - test[0, 0] = 1; - test[1, 0] = 1; - test[6, 0] = 1; - test[3, 1] = 1; - test[0, 2] = 1; - test[1, 2] = 1; - test[3, 2] = 1; - test[5, 4] = 1; - test[6, 4] = 1; - test[1, 5] = 1; - test[2, 5] = 1; - test[5, 5] = 1; - test[1, 7] = 1; - test[3, 7] = 0; - test[6, 7] = 1; - - board.boardState = test; - //Board.setBoardState(test); + Board = b; + CreateUI(); - //*/ + Board.boardState[1, 1] = 1; + Board.boardState[2, 2] = 0; } - private void CreateUI() + private void CreateUI() //sets up ui elements { Point boardP = new Point(220, 30); Size boardS = new Size(400, 400); @@ -80,15 +87,18 @@ 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.Generate(); - board.Draw(graphics, boardP, boardS); + Board.Generate(); + Board = Board; }; CreateButton(hintbutton); hintbutton.Text = "Hint"; @@ -96,28 +106,94 @@ namespace PuzzlePlayer_Namespace { board.Draw(graphics, boardP, boardS); MessageBox.Show("Hint: geef op"); + Board = Board; }; CreateButton(solvebutton); solvebutton.Text = "Solve"; solvebutton.MouseClick += (object sender, MouseEventArgs mea) => { - board.Solve(false); - board.Draw(graphics, boardP, boardS); + 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, 400); - 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() + private void UpdateUI() //resizes the boardspace rectangle and updates the rest of the ui around the new boardspace size + { + 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, 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, + //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. + } + 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 { - generatebutton.Location = new Point(this.Width - 160, 30); - hintbutton.Location = new Point(this.Width - 160, 130); - solvebutton.Location = new Point(this.Width - 160, 230); - informationbox.Location = new Point(40, 30); + 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"); } } }