diff --git a/PuzzlePlayer/Binair.cs b/PuzzlePlayer/Binair.cs deleted file mode 100644 index 9b37ee3000619a7f4e9d871496aa137e43adbcd7..0000000000000000000000000000000000000000 --- a/PuzzlePlayer/Binair.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PuzzlePlayer_Namespace -{ - internal class Binair : Board - { - public int[,] boardState { get; set; } - - public static void Generate() - { - throw new NotImplementedException(); - } - - public static void Solve() - { - throw new NotImplementedException(); - } - } -} diff --git a/PuzzlePlayer/Binary.cs b/PuzzlePlayer/Binary.cs new file mode 100644 index 0000000000000000000000000000000000000000..833053ea16951dbac0f07230e83f54de2cc7dc44 --- /dev/null +++ b/PuzzlePlayer/Binary.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Runtime.CompilerServices; + +namespace PuzzlePlayer_Namespace +{ + /* The binair board consist of 1 and 0 + * This means that the possible states a cell can be are: empty, one or zero + * To specify this in an int[,] array we will use -1 for an empty space, + * 0 for a space with a zero and 1 for a space with a one + * The empty space is a constant defined in the abstract Board class + */ + + internal class Binary : Board + { + + // constructor with baordSize parameter (default is set to 8 but can be changed) + public Binary(int boardSize = 8) + { + // 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"; + + // clear the board (fill it in with -1) + Clear(); + } + + public void Draw (Graphics gr, Point p, Size s) + { + for (int i = 0; i<boardState.GetLength(0); i++) + { + for(int j = 0; j<boardState.GetLength(1); j++) + { + gr.DrawRectangle(Pens.Beige, p.X+i*s.Width, p.Y+j*s.Height, s.Width, s.Height); + if (boardState[i,j] == 0) + { + gr.DrawEllipse(Pens.White, + (int)(p.X+((double)i+0.25)*s.Width), + (int)(p.Y+((double)j+0.25)*s.Height), + s.Width/2,s.Height/2); + return; + } + if (boardState[i, j] == 1) + { + gr.DrawEllipse(Pens.Black, + (int)(p.X + ((double)i + 0.25) * s.Width), + (int)(p.Y + ((double)j + 0.25) * s.Height), + s.Width / 2, s.Height / 2); + } + } + } + } + + private void Clear() + { + int size = boardState.GetLength(0); + // fill the board with empty spaces (-1) + for (int i = 0; i < size; i++) + { + for (int j = 0; j < size; j++) + boardState[i, j] = emptySpace; + } + } + + /* this static methode returns true if the board is valid in a few steps + 1: It checks if the size from boardToCheck is equal to the size from the boardState variable + 2: it loops though the board and checks if one of the spaces contains something other than a -1,0 or 1 + */ + public override bool IsBoardValid(int[,] boardToCheck) + { + // check if the size is NOT the same + if(!(boardToCheck.GetLength(0) == boardState.GetLength(0) && boardToCheck.GetLength(1) == boardState.GetLength(1))) + return false; + + + // check if any of the spaces doesn't contain a -1,0 or 1 + for(int i = 0;i < boardToCheck.GetLength(0);i++) + { + for(int j = 0; j < boardToCheck.GetLength(1);j++) + { + switch(boardToCheck[i,j]) + { + case -1: + case 0: + case 1: + break; + default: + return false; + } + } + } + + return true; + } + + public override void Generate() + { + throw new NotImplementedException(); + } + + protected override List<int[,]> GetSolveList(int[,] boardToSolve) + { + List<int[,]> result = new List<int[,]>(); + + for (int i = 0; i < boardToSolve.GetLength(0); i++) + for (int j = 0; j < boardToSolve.GetLength(1); + { + int[,] move = CheckMove(i, j, boardToSolve); + if (move != null) + result.Add(move); + } + + + + return result; + } + + private int[,] CheckMove(int x, int y, int[,] boardToSolve) + { + bool validForZero = false; + bool validForOne = false; + + // empty check + if (boardToSolve[x, y] != emptySpace) + return null; + + // loop two times for checking 0 and 1 + for (int i = 0; i <= 1; i++) + { + bool valid = false; + + // middle check + valid = MiddleCheck(x, y, boardToSolve, i); + + // side check + + // even 1 and 0 in one row and colum + + + } + + + } + + // 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 bool MiddleCheck(int x, int y, int[,] b, int checkFor) + { + int opposite; + + if (checkFor == 0) + opposite = 1; + else + opposite = 0; + + // first check if x-1 and x+1 aren't out of bounds + // after that do the check if the move is valid + if(!(x-1 < 0 || x+1 > b.GetLength(0))) + if (b[x - 1, y] == opposite && b[x + 1, y] == opposite) + return true; + + // same for y + if (!(y - 1 < 0 || y + 1 > b.GetLength(1))) + if (b[x, y - 1] == opposite && b[x, y + 1] == opposite) + return true; + + // return false if nothing was found + return false; + } + + private bool SideCheck(int x, int y, int[,] b, int checkFor) + { + int opposite; + + if (checkFor == 0) + opposite = 1; + else + opposite = 0; + + if (!(x - 2 < 0 || x + 2 > b.GetLength(0))) + if ((b[x-2,y] == opposite && b[x-1,y] == opposite) || (b[x + 2, y] == opposite && b[x + 1, y] == opposite)) + return true; + + if (!(y - 2 < 0 || y + 2 > b.GetLength(1))) + if ((b[x, y - 2] == opposite && b[x, y - 1] == opposite) || (b[x, y + 2] == opposite && b[x, y + 1] == opposite)) + return true; + + return false; + } + + } +} \ No newline at end of file diff --git a/PuzzlePlayer/Board.cs b/PuzzlePlayer/Board.cs index de744f510cbc6db73c35471646307b26f4227b0a..52484ce853e54b737b4836d591dc7b6fd1f1443a 100644 --- a/PuzzlePlayer/Board.cs +++ b/PuzzlePlayer/Board.cs @@ -1,18 +1,101 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection.Metadata; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace PuzzlePlayer_Namespace { - public interface Board + public enum SOLUTIONS { - int[,] boardState { get; set; } + NONE = 0, + UNIQUE = 1, + MULTIPLE = 2 + } + + /* + * This abstract class can be used to implement any kind of puzzle + * One thing that is common for all puzzles is that a empty space is equal to -1 + * GetSolveList and IsBoardValid need to be overwriten in a subclass of Board + */ + public abstract class Board + { + protected const int emptySpace = -1; // for every puzzle -1 represents a empty space + public string description; + + // a property for getting and setting the boardsstate. The boardstate should only be setted if the imputed int[,] is valid. + public int[,] boardState + { + get { return boardState; } + set { setBoardState(value); } + } + + // checks if the board is valid and solvable before setting the variable. + private bool setBoardState(int[,] newState) + { + int[,] copy = boardState; + boardState = newState; + if (IsBoardValid(newState) && Solve() == SOLUTIONS.UNIQUE) + return true; + else + { + boardState = copy; + return false; + } + } + + // a methode for solving the whole board. It uses the private SolveStep methode untill the whole board is solved + public SOLUTIONS Solve() + { + // two variables for storing the result and the next solveStep + int[,] result = boardState; + int[,] step; + + // keep looping until the SolveStep returns null + // if SolveStep returns null that could mean that either the whole board is solved or it is imposible to solve the board + while ((step = SolveStep(result)) != null) + { + result = step; + } + + // check if the whole board is filled + // if not then the methode will return null because it is imposible to solve the board + for (int i = 0; i < result.GetLength(0); i++) + for (int j = 0; j < result.GetLength(1); j++) + if (result[i, j] == emptySpace) + { + return SOLUTIONS.NONE; + } + + boardState = result; + return SOLUTIONS.UNIQUE; + } + + // abstract methode for solving one step + private int[,] SolveStep(int[,] currentBoardState) + { + // get a list with all the possible moves + List<int[,]> moves = GetSolveList(currentBoardState); + + // if there are no moves found then null is returned + if (moves.Count == 0) + return null; + + // return one of the possible moves + // (if the first one is always chosen than that may lead to expected behavior. For example if the possible moves are checked in a certain order) + Random rnd = new Random(); + return moves[rnd.Next(0, moves.Count - 1)]; + } + + // a abstract methode to get a list of all possible moves + protected abstract List<int[,]> GetSolveList(int[,] boardToSolve); - public abstract static void Solve(); + // abstract methode for generating a random board + public abstract void Generate(); - public abstract static void Generate(); + // abstract methode for checking if a imputed boardstate is valid + public abstract bool IsBoardValid(int[,] boardToCheck); } } diff --git a/PuzzlePlayer/PuzzlePlayer.cs b/PuzzlePlayer/PuzzlePlayer.cs index 3861f47aa122f96d87fd0c782c0e7625034bf34c..634cbef5a7e23136d8429d5935092e2402cffbbf 100644 --- a/PuzzlePlayer/PuzzlePlayer.cs +++ b/PuzzlePlayer/PuzzlePlayer.cs @@ -33,7 +33,7 @@ namespace PuzzlePlayer_Namespace private void SetUpPuzzleForms() { - puzzleForms.Add(new PuzzleForm(new Binair())); + puzzleForms.Add(new PuzzleForm(new Binary())); } private void SetUpUI()