From a76881537863ad7a6bffcb36d20fcd0dd59e3484 Mon Sep 17 00:00:00 2001
From: bionic85 <144353436+bionic85@users.noreply.github.com>
Date: Fri, 22 Nov 2024 15:09:58 +0100
Subject: [PATCH] het doet ten minste iets

werkt nog niet tho
---
 PuzzlePlayer/Binary.cs     | 126 ++++++++++++++++++++++++++++++-------
 PuzzlePlayer/PuzzleForm.cs |  13 ++--
 2 files changed, 111 insertions(+), 28 deletions(-)

diff --git a/PuzzlePlayer/Binary.cs b/PuzzlePlayer/Binary.cs
index 9befa72..af85747 100644
--- a/PuzzlePlayer/Binary.cs
+++ b/PuzzlePlayer/Binary.cs
@@ -100,8 +100,6 @@ namespace PuzzlePlayer_Namespace
                 }
             }
 
-            //check if there aren't any
-
             return true;
         }
 
@@ -109,31 +107,102 @@ namespace PuzzlePlayer_Namespace
         public override void Generate()
         {
             // start with a clear board
-            int[,] result = GetClearBoard(BoardState.GetLength(0));
+            int[,] startBoard = GetClearBoard(BoardState.GetLength(0));
+
+            // generate a board
+            int[,] generatedBoard = BackTrackAlgorithm(startBoard);
+
+            if (setBoardState(generatedBoard))
+                Debug.WriteLine("succesfully generated a board");
+            else
+                Debug.WriteLine("failed to generate a board");
+            
+        }
 
-            // keep adding stuff until it works?
-            while (!setBoardState(result))
+
+        // generates a random board with a backtracking algorithm
+        // After searching online about what the best way is to make a random puzzle generator a lot of people pointed towards a backtracking algorithm
+        // I found the information about what a backtracking algorithm is here: https://www.geeksforgeeks.org/introduction-to-backtracking-2/
+        // But i wrote all the code myself
+        private static int[,] BackTrackAlgorithm(int[,] board)
+        {
+            // check if the board is complete. if so then we can return the result
+            if (IsBoardCompletlyFilledIn(board))
+                return board;
+
+            // get all the possible choices and per choice do the backtrackAlgorithm
+            List<Move> choices = GetChoices(board);
+
+            // if there aren't any solutions then that could mean that the whole board is filled in, or that we are stuck and need to backtrack
+            // and because we already did a check if the board is completly filled-in, we know that we need to backtrack
+            if (choices.Count == 0)
+                return null; // backtrack
+
+            // shuffle the choices in the list around to get different results every time
+            // First answer from: https://stackoverflow.com/questions/273313/randomize-a-listt
+            Random random = new Random();
+            int n = choices.Count;
+            while (n > 1)
             {
-                //add random things until it works?
-                throw new NotImplementedException();
+                n--;
+                int rand = random.Next(n + 1);
+                Move copy = choices[rand];
+                choices[rand] = choices[n];
+                choices[n] = copy;
+            }
+
+            // do the algorithm for every move
+            foreach (Move m in choices)
+            {
+                int[,] newBoard = board;
+                newBoard[m.x,m.y] = m.changeTo;
+                
+                int[,] result = BackTrackAlgorithm(newBoard); // recursion for every move
 
+                if(result != null)
+                    return result;
             }
+
+            // if all choices fail then we should also return null
+            return null;
         }
 
-        // checks if the move is valid
-        private static bool IsValidMove(Move m, int[,] board)
+        // get all the possible choices
+        private static List<Move> GetChoices(int[,] board)
         {
-            int opposite;
-            if (m.changeTo == 0)
-                opposite = 1;
-            else
-                opposite = 0;
+            List<Move> choices = new List<Move>();
 
-            return MiddleCheck(m.x, m.y, board, opposite) ||
-                    SideCheck(m.x, m.y, board, opposite) ||
-                    EvenCheck(m.x, m.y, board, opposite);
+            // loop for both 1 and 0
+            for (int checkFor = 0; checkFor <= 1; checkFor++)
+            {
+                // check foreach cell if it doesn't violate the rules
+                for (int i = 0; i < board.GetLength(0); i++)
+                    for (int j = 0; j < board.GetLength(1); j++)
+                    {
+                        // if the checked space is already filled in than it is not a valid move
+                        if (board[i, j] != emptySpace)
+                            continue;
+
+                        // if one of the checks succeeds then it is an invalid move
+                        if(DoAllChecks(i,j,board,checkFor))
+                            continue;
+
+                        // if all checks pass then add the move to the list
+                        choices.Add(new Move(i, j, checkFor));
+                    }
+            }
+            return choices;
+        }
+
+        private static bool IsBoardCompletlyFilledIn(int[,] board)
+        {
+            for (int i = 0; i < board.GetLength(0); i++)
+                for (int j = 0; j < board.GetLength(1); j++)
+                    if (board[i, j] == emptySpace) // if the space is equal to an empty space then the board is not filled in
+                        return false;
+
+            return true;
         }
-        
 
         // gets a list with all the possible moves
         protected override List<Move> GetSolveList(int[,] boardToSolve)
@@ -153,9 +222,6 @@ namespace PuzzlePlayer_Namespace
 
         private Move? CheckMove(int x, int y, int[,] boardToSolve)
         {
-            bool validForZero = false;
-            bool validForOne = false;
-
             // empty check
             if (boardToSolve[x, y] != emptySpace)
                 return null;
@@ -163,17 +229,29 @@ namespace PuzzlePlayer_Namespace
             // loop two times for checking 0 and 1
             for (int i = 0; i <= 1; i++)
             {
-                Move m = new Move(x, y, i);
+                int opposite;
+                if (i == 0)
+                    opposite = 1;
+                else
+                    opposite = 0;
 
                 // check if it is a valid move
-                if(IsValidMove(m, boardToSolve))
-                    return m;
+                if (DoAllChecks(x, y, boardToSolve, opposite))
+                    return new Move(x, y, i);
             }
 
             // if both 0 and 1 fail, then the move is invalid and null is returned
             return null;
         }
 
+        // shorthand for all the checks in one function
+        private static bool DoAllChecks(int x, int y, int[,] board, int checkFor)
+        {
+            return MiddleCheck(x, y, board, checkFor) ||
+                    SideCheck(x, y, board, checkFor) ||
+                    EvenCheck(x, y, board, checkFor);
+        }
+
         // check if the space is surrounded on both sides by the same number. If it is, the checked space should be the opposite number
         private static bool MiddleCheck(int x, int y, int[,] b, int checkFor)
         {
diff --git a/PuzzlePlayer/PuzzleForm.cs b/PuzzlePlayer/PuzzleForm.cs
index 930b178..e9796b6 100644
--- a/PuzzlePlayer/PuzzleForm.cs
+++ b/PuzzlePlayer/PuzzleForm.cs
@@ -47,6 +47,7 @@ namespace PuzzlePlayer_Namespace
             };
             
             // example board: https://imgur.com/spyYaPl
+            /*
             board.boardState[0, 0] = 1;
             board.boardState[1, 0] = 1;
             board.boardState[6, 0] = 1;
@@ -62,10 +63,13 @@ namespace PuzzlePlayer_Namespace
             board.boardState[1, 7] = 1;
             board.boardState[3, 7] = 0;
             board.boardState[6, 7] = 1;
-
+            */
         }
         private void CreateUI()
         {
+            Point boardP = new Point(220, 30);
+            Size boardS = new Size(400, 400);
+
             void CreateButton(Button b)
             {
                 Controls.Add(b);
@@ -77,7 +81,8 @@ namespace PuzzlePlayer_Namespace
             generatebutton.Text = "Generate";
             generatebutton.MouseClick += (object sender, MouseEventArgs mea) => 
             {
-                //board = Board.Generate();
+                board.Generate();
+                board.Draw(graphics, boardP, boardS);
             };
             CreateButton(hintbutton);
             hintbutton.Text = "Hint";
@@ -89,8 +94,8 @@ namespace PuzzlePlayer_Namespace
             solvebutton.Text = "Solve";
             solvebutton.MouseClick += (object sender, MouseEventArgs mea) =>
             {
-                board.Solve();
-                board.Draw(graphics, new Point(220, 30), new Size(400, 400));
+                board.Solve(false);
+                board.Draw(graphics, boardP, boardS);
             };
 
             Controls.Add(informationbox);
-- 
GitLab