diff --git a/PuzzlePlayer/Board.cs b/PuzzlePlayer/Board.cs index bab69e95c2da32ba0f8244fdb9fc065cb6fa7a30..caefc517f6c8e48972453b7f1ebe3a5b0aae6b8c 100644 --- a/PuzzlePlayer/Board.cs +++ b/PuzzlePlayer/Board.cs @@ -126,7 +126,7 @@ namespace PuzzlePlayer_Namespace } // changes tile P to value X - public abstract bool TileInput(Point? p, Keys k); + public virtual bool TileInput(Point? p, Keys k) { return false; } // performs a left/right (X) click on tile P, changing its value public virtual bool TileClick(Point p, int x) { return false; } diff --git a/PuzzlePlayer/Maze.cs b/PuzzlePlayer/Maze.cs index f1afe6fdfa2ea6b7be3374f33a0cf18c324088d6..a7d0467483b4d900990f9172bf1df0779541292e 100644 --- a/PuzzlePlayer/Maze.cs +++ b/PuzzlePlayer/Maze.cs @@ -40,14 +40,14 @@ namespace PuzzlePlayer_Namespace Point playerPos; List<Point> shortestPath; - public Maze(int sizeX = 6, int sizeY = 4) + public Maze(int sizeX = 32, int sizeY = 24) { drawFactor = 1; // init all 2D array's Reset(sizeX, sizeY); lastGeneratedBoard = GetClearBoard(sizeX, sizeY); - description = "Maze is played on a grid containing walls. You need to navigate through the maze until you reach the end. Use the arrow keys or WASD to move the blue square to the end goal"; + description = "Maze is played on a grid containing walls. You have to navigate through the maze until you reach the end. Use the arrow keys or WASD to move the blue square to the end!"; } private void Reset(int sizeX, int sizeY) diff --git a/PuzzlePlayer/Minesweeper.cs b/PuzzlePlayer/Minesweeper.cs index 06422e78007015ff993eaeb776dd8a49491ebd54..23a5249607404c9639144713b7601926332b11ef 100644 --- a/PuzzlePlayer/Minesweeper.cs +++ b/PuzzlePlayer/Minesweeper.cs @@ -14,9 +14,9 @@ namespace PuzzlePlayer_Namespace { private static Random random = new Random(); - private bool[,] mineState; + private bool[,] mineState; private bool isFirstClick; - public Minesweeper(int width = 6, int height = 6) + public Minesweeper(int width = 16, int height = 12) { if (width <= 1 || height <= 1) throw new ArgumentOutOfRangeException(); // breedte 1 mag niet door optimisatie bij Reveal() boardState = GetClearBoard(width, height); @@ -24,7 +24,7 @@ namespace PuzzlePlayer_Namespace mineState = new bool[width, height]; isFirstClick = false; - description = "zoek het uit"; + description = "In Minesweeper, the number of a tile indicates the amount of adjacent mines.\r\n◠You can place flags on suspected mines with right-click\r\n◠Unlike the classic version of minesweeper, there is not a set number of mines and you will survive after clicking on a mine, at the cost of gaining less points \r\n◠The first click is always safe!"; drawFactor = 1; } @@ -71,7 +71,7 @@ namespace PuzzlePlayer_Namespace case -2: gr.DrawString("⚑", new Font("Verdana", stringsize), - Brushes.Black, + Brushes.DarkRed, (int)(r.X + ((double)i + 0.5) * tilesize.Width), (int)(r.Y + ((double)j + 0.5) * tilesize.Height), stringFormat); @@ -81,13 +81,26 @@ namespace PuzzlePlayer_Namespace gr.DrawString("✸", new Font("Verdana", stringsize), Brushes.Black, - (int)(r.X + ((double)i + 0.5) * tilesize.Width), - (int)(r.Y + ((double)j + 0.5) * tilesize.Height), + (int)(r.X + ((double)i + 0.52) * tilesize.Width), + (int)(r.Y + ((double)j + 0.54) * tilesize.Height), stringFormat); break; } } + /*gr.DrawLine(Pens.Red, + (int)(r.X + ((double)i + 0.5) * tilesize.Width), + (int)(r.Y + ((double)j + 0.0) * tilesize.Height), + (int)(r.X + ((double)i + 0.5) * tilesize.Width), + (int)(r.Y + ((double)j + 1.0) * tilesize.Height) + ); + gr.DrawLine(Pens.Red, + (int)(r.X + ((double)i + 0.0) * tilesize.Width), + (int)(r.Y + ((double)j + 0.5) * tilesize.Height), + (int)(r.X + ((double)i + 1.0) * tilesize.Width), + (int)(r.Y + ((double)j + 0.5) * tilesize.Height) + );*/ + gr.DrawRectangle(Pens.DarkGray, r.X + i * tilesize.Width, r.Y + j * tilesize.Height, @@ -102,7 +115,7 @@ namespace PuzzlePlayer_Namespace boardState = GetClearBoard(mineState.GetLength(0), mineState.GetLength(1)); for (int i = 0; i < mineState.GetLength(0); i++) for (int j = 0; j < mineState.GetLength(1); j++) { - mineState[i, j] = random.Next(6) == 0; // het getal P in de Next methode geeft elke tile een 1/P kans om een bom te zijn + mineState[i, j] = random.Next(6) == 0; // the number P, the Next method parameter indicates that there is a 1/P chance for a tile to have a mine } isFirstClick = true; } @@ -111,10 +124,6 @@ namespace PuzzlePlayer_Namespace { return SOLUTIONS.UNIQUE; } - public override bool TileInput(Point? p, Keys k) - { - return false; - } public override bool TileClick(Point p, int x) { if (x == 0) @@ -128,7 +137,7 @@ namespace PuzzlePlayer_Namespace } return false; - bool Reveal(Point p, bool ForceOpen) + bool Reveal(Point p, bool ForceOpen) //opens the cell at the given Point and returns whether or not the game is finished after reveal. ForceOpen dictates whether or not the cell should be opened if a flag has been placed on it. { if (ForceOpen && boardState[p.X, p.Y] == -2) { boardState[p.X, p.Y] = emptySpace; Reveal(p, false); } if (boardState[p.X, p.Y] != emptySpace) return false; @@ -144,21 +153,35 @@ namespace PuzzlePlayer_Namespace return false; } else { if (isFirstClick) isFirstClick = false; } int result = 0; - List<Size> directions = new List<Size> { new(-1, -1), new(0, -1), new(1, -1), new(-1, 0), - new(1, 0), new(-1, 1), new(0, 1), new(1, 1)}; - if (p.X == 0) { directions.Remove(new(-1, -1)); directions.Remove(new(-1, 0)); directions.Remove(new(-1, 1)); } - else { if (p.X == boardState.GetLength(0) - 1) { directions.Remove(new(1, -1)); directions.Remove(new(1, 0)); directions.Remove(new(1, 1)); } } - if (p.Y == 0) { directions.Remove(new(-1, -1)); directions.Remove(new(0, -1)); directions.Remove(new(1, -1)); } - else { if (p.Y == boardState.GetLength(1) - 1) { directions.Remove(new(-1, 1)); directions.Remove(new(0, 1)); directions.Remove(new(1, 1)); } } + List<Size> directions = GetAdjacents(p); foreach (Size s in directions) if (mineState[(p + s).X, (p + s).Y]) result++; boardState[p.X, p.Y] = result; if (result == 0) foreach (Size s in directions) Reveal(p + s, true); return IsBoardSolved(); } - bool Chord(Point p) + bool Chord(Point p) //attempts to execute a "chord" on cell P, assumes P is legal to "chord" on, but behaves normally if not legal anyway + { + List<Size> directions = GetAdjacents(p); + int adjacents = directions.Count; + for (int i = 0; i < directions.Count; i++) + { + Point cell = p + directions[i]; + if (boardState[cell.X, cell.Y] == -2 || boardState[cell.X, cell.Y] == -3) { directions.RemoveAt(i); i--; } + } + if (adjacents - directions.Count == boardState[p.X, p.Y]) { foreach (Size s in directions) Reveal(p + s, true); return IsBoardSolved(); } + else return false; + } + + List<Size> GetAdjacents(Point p) //returns direction vectors that point to all valid neighbors of cell P { - return false; + List<Size> res = new List<Size> { new(-1, -1), new(0, -1), new(1, -1), new(-1, 0), + new(1, 0), new(-1, 1), new(0, 1), new(1, 1)}; + if (p.X == 0) { res.Remove(new(-1, -1)); res.Remove(new(-1, 0)); res.Remove(new(-1, 1)); } + else { if (p.X == boardState.GetLength(0) - 1) { res.Remove(new(1, -1)); res.Remove(new(1, 0)); res.Remove(new(1, 1)); } } + if (p.Y == 0) { res.Remove(new(-1, -1)); res.Remove(new(0, -1)); res.Remove(new(1, -1)); } + else { if (p.Y == boardState.GetLength(1) - 1) { res.Remove(new(-1, 1)); res.Remove(new(0, 1)); res.Remove(new(1, 1)); } } + return res; } } diff --git a/PuzzlePlayer/Properties/Settings.Designer.cs b/PuzzlePlayer/Properties/Settings.Designer.cs index 38a04c7c48d469a2fdf8ee52617acff3610b14a9..9013f5f267c41831d6319a4e6bd7123d7aab45af 100644 --- a/PuzzlePlayer/Properties/Settings.Designer.cs +++ b/PuzzlePlayer/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace PuzzlePlayer_Namespace.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.11.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.12.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/PuzzlePlayer/PuzzleForm.cs b/PuzzlePlayer/PuzzleForm.cs index 7b9284d00aae6f6d124d0dd28221b681361abbb1..dc9104166bdcbc58d71feae511ac25c4517b1a76 100644 --- a/PuzzlePlayer/PuzzleForm.cs +++ b/PuzzlePlayer/PuzzleForm.cs @@ -55,13 +55,7 @@ namespace PuzzlePlayer_Namespace titlebox = new Label(); informationbox = new Label(); boardspace = new Rectangle(220, 55, 400, 400); - this.Paint += (object o, PaintEventArgs pea) => - { - board.Draw(bufferedGraphics.Graphics, boardspace); - //bufferedGraphics.Graphics.FillRectangle(Brushes.LightCoral, boardspace.X, boardspace.Y, this.Width - 350, this.Height - 125); - //bufferedGraphics.Graphics.FillRectangle(Brushes.DarkRed, boardspace); - bufferedGraphics.Render(); - }; + this.Paint += (object o, PaintEventArgs pea) => Draw(); this.Resize += (object o, EventArgs ea) => UpdateUI(); this.Move += (object o, EventArgs ea) => this.Focus(); this.PreviewKeyDown += (object o, PreviewKeyDownEventArgs e) => e.IsInputKey = true; @@ -80,6 +74,13 @@ namespace PuzzlePlayer_Namespace CreateUI(); } + private void Draw() + { + board.Draw(bufferedGraphics.Graphics, boardspace); + //bufferedGraphics.Graphics.FillRectangle(Brushes.LightCoral, boardspace.X, boardspace.Y, this.Width - 350, this.Height - 125); + //bufferedGraphics.Graphics.FillRectangle(Brushes.DarkRed, boardspace); + bufferedGraphics.Render(); + } private void CreateUI() //sets up ui elements { MenuStrip menuStrip = new MenuStrip @@ -140,28 +141,29 @@ namespace PuzzlePlayer_Namespace generatebutton.MouseClick += (object sender, MouseEventArgs mea) => { Board.Generate(); - this.Invalidate(); + Draw(); }; CreateButton(hintbutton); hintbutton.Text = "Hint"; hintbutton.MouseClick += (object sender, MouseEventArgs mea) => { MessageBox.Show("Hint: geef op"); - this.Invalidate(); + Draw(); }; CreateButton(solvebutton); solvebutton.Text = "Solve"; solvebutton.MouseClick += (object sender, MouseEventArgs mea) => { Board.Solve(false); - this.Invalidate(); + if (Board.IsBoardSolved()) Win(); + Draw(); }; CreateButton(restartbutton); restartbutton.Text = "Restart"; restartbutton.MouseClick += (object sender, MouseEventArgs mea) => { Board.Restart(); - this.Invalidate(); + Draw(); }; //CreateButton(UPDATEBUTTON); @@ -208,7 +210,7 @@ namespace PuzzlePlayer_Namespace 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(Math.Max(6,gamesize.Width*TileLength), Math.Max(6,gamesize.Height*TileLength)); //dw dw + return new Size(Math.Max(2*gamesize.Width,gamesize.Width*TileLength), Math.Max(2*gamesize.Height,gamesize.Height*TileLength)); //dw dw //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. @@ -230,11 +232,11 @@ namespace PuzzlePlayer_Namespace return; case Keys.H: MessageBox.Show("Hint: geef op"); - this.Invalidate(); + Draw(); return; case Keys.Space: Board.Solve(false); - this.Invalidate(); + Draw(); return; case Keys.Up: case Keys.Left: @@ -245,7 +247,7 @@ namespace PuzzlePlayer_Namespace case Keys.S: case Keys.D: if (Board.TileInput(null, k)) Win(); - this.Invalidate(); + Draw(); return; } Point tile = GetTile(new Size(Board.boardState.GetLength(0), Board.boardState.GetLength(1)), boardspace, Control.MousePosition); @@ -255,28 +257,28 @@ namespace PuzzlePlayer_Namespace { case Keys.Back: Board.boardState[tile.X, tile.Y] = Board.emptySpace; - this.Invalidate(); + Draw(); return; case Keys.LButton: case Keys.RButton: if (Board.TileClick(tile, (int)k - 1)) Win(); - this.Invalidate(); + Draw(); return; case Keys.OemOpenBrackets: case Keys.OemCloseBrackets: if (Board.TileClick(tile, ((int)k - 219)/2)) Win(); - this.Invalidate(); + Draw(); return; default: if (Board.TileInput(tile, k)) Win(); - this.Invalidate(); + Draw(); return; } } public void Win() { - this.Invalidate(); + Draw(); MessageBox.Show("oi oi oi"); } } diff --git a/PuzzlePlayer/PuzzlePlayer.cs b/PuzzlePlayer/PuzzlePlayer.cs index b1336b8492e5a5dd6d218ec0297caf5b53aa1c8a..b516283e058b5eab1c4a3968cc2a0d59b4ad2485 100644 --- a/PuzzlePlayer/PuzzlePlayer.cs +++ b/PuzzlePlayer/PuzzlePlayer.cs @@ -11,7 +11,6 @@ namespace PuzzlePlayer_Namespace { internal static void Main(string[] args) { - //Application.Run(new ShopMenu()); Application.Run(new MainForm()); } } diff --git a/PuzzlePlayer/Sudoku.cs b/PuzzlePlayer/Sudoku.cs index e2424492c560e38f045d5857ebff38c792f281d5..85184d1e3b46831fd8e0f025141992e666f88c23 100644 --- a/PuzzlePlayer/Sudoku.cs +++ b/PuzzlePlayer/Sudoku.cs @@ -12,8 +12,8 @@ namespace PuzzlePlayer_Namespace { internal class Sudoku : Board { - private static int boardLength; - private static int rootBoardLength; + private readonly int boardLength; + private readonly int rootBoardLength; private static Random random = new Random(); private double removeDensity = 0.5; public Sudoku(int boardSize = 4) @@ -37,44 +37,32 @@ namespace PuzzlePlayer_Namespace }; Size tilesize = new Size(r.Width / boardState.GetLength(0), r.Height / boardState.GetLength(1)); Pen border = new Pen(Color.Black, 2); + Brush textcolor; 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.LightGray, r.X + i * tilesize.Width, r.Y + j * tilesize.Height, tilesize.Width, tilesize.Height); - if (boardState[i,j] != -1) + if (boardState[i,j] != emptySpace) { - if (lastGeneratedBoard[i, j] != Board.emptySpace) - { - gr.DrawString( - (boardState[i, j]).ToString(), - new Font("Arial", tilesize.Width / 2), - Brushes.Black, - (int)(r.X + (i + 0.27) * tilesize.Width + tilesize.Width / 4), - (int)(r.Y + (j + 0.33) * tilesize.Height + tilesize.Height / 4), - format); - } - else - { - gr.DrawString( - (boardState[i, j]).ToString(), - new Font("Arial", tilesize.Width / 2), - Brushes.DarkBlue, - (int)(r.X + (i + 0.27) * tilesize.Width + tilesize.Width / 4), - (int)(r.Y + (j + 0.33) * tilesize.Height + tilesize.Height / 4), - format); - } + if (lastGeneratedBoard[i, j] == Board.emptySpace) textcolor = Brushes.DarkBlue; else textcolor = Brushes.Black; + gr.DrawString( + (boardState[i, j]).ToString(), + new Font("Arial", tilesize.Width / 2), + textcolor, + (int)(r.X + (i + 0.52) * tilesize.Width), + (int)(r.Y + (j + 0.54) * tilesize.Height), + format); } } } - for (int i = 1; i < Math.Sqrt(boardState.GetLength(0)); i++) + for (int i = 1; i < Math.Sqrt(boardState.GetLength(0)); i++) //draws box lines { gr.DrawLine(Pens.Black, r.X + i * (int)Math.Sqrt((double)boardState.GetLength(0)) * tilesize.Width, @@ -184,7 +172,7 @@ namespace PuzzlePlayer_Namespace { for (int i = 0; i < boardLength; i++) { - if(num == boardState[i, j]) + if( num == boardState[i, j]) { return true; } @@ -265,7 +253,7 @@ namespace PuzzlePlayer_Namespace if (p == null) return false; int num = (int)k - 48; if (num >= 1 && num <= boardLength) boardState[((Point)p).X, ((Point)p).Y] = num; - return false; + return IsBoardSolved(); } public override bool TileClick(Point p, int x) { @@ -317,6 +305,33 @@ namespace PuzzlePlayer_Namespace } } + public override bool IsBoardSolved() + { + foreach (int n in boardState) if (n == emptySpace) return false; //first checks if all squares have been filled to prevent algoritm from requiring extra checks and running after each input + + for (int i = 0; i < boardLength; i++) // simultaneous row/column check + { + HashSet<int> SetColumn = []; + HashSet<int> SetRow = []; + for (int j = 0; j < boardLength; j++) + { + if (!SetColumn.Add(boardState[i, j])) return false; + if (!SetRow.Add(boardState[j, i])) return false; + } + } + + for (int i = 0; i < rootBoardLength; i++) for (int j = 0; j < rootBoardLength; j++) //box check + { + HashSet<int> Set = []; + for (int a = 0; a < rootBoardLength; a++) for (int b = 0; b < rootBoardLength; b++) + { + if (!Set.Add(boardState[i * rootBoardLength + a, j * rootBoardLength + b])) return false; + } + } + + return true; + } + private void RemoveSpaces(int k) { for(int i = 0; i < k; i++)