/// <summary> /// FillSingleChoices iterates through empty cells. For any empty cell /// with only one possible value, it fills in that value. /// </summary> /// <param name="grid">Current state of grid</param> private void FillSingleChoices(PuzzleGrid grid) { bool anyChanges = false; //Set if cell value set int numChoices; //Number of choices found do //While changes have been made AND grid is not solved { anyChanges = false; for (int i = 0; i < 9; i++) //Iterate through every row { for (int j = 0; j < 9; j++) //Iterate through every column { if (grid.Grid[i, j] == 0) { //Get number of choices available in grid[i, j] numChoices = ListPossible(i, j, grid); if (numChoices == 1) //If only one choice set value { grid.UserSetCell(i, j, FirstTrue()); //Changes made, anyChanges = true anyChanges = (grid.Grid[i, j] != 0); } } } } } while(anyChanges == true && !IsSolved(grid)); }
/// <summary> /// IsPossible returns true if IsInRow, IsInCol & IsIn3x3 return false /// </summary> /// <param name="g">Current state of the grid</param> /// <param name="row">row of target cell</param> /// <param name="col">column of target cell</param> /// <param name="value">value being sought</param> /// <returns>True if value can occupy cell at [row, col]</returns> private bool IsPossible(PuzzleGrid g, int row, int col, int value) { //Return true if value can go into [row, col] now bool result; result = (!IsInRow(g, row, value) && ! IsInCol(g, col, value) && !IsIn3X3(g, row, col, value)); return result; }
/// <summary> /// This function takes a PuzzleGrid and updates the game board values to the values in the PuzzleGrid. /// </summary> /// <param name="grid">The puzzle gird.</param> private void SetPuzzleGrid(PuzzleGrid grid) { for (int i = 0; i < GameBoardRows; i++) { for (int j = 0; j < GameBoardCols; j++) { var box = (TextBox)FindName(GetTextBoxNameFromRowColumn(i + 1, j + 1)); if (box != null) { box.Style = (Style)(Resources["GridElement"]); if (grid.Grid[i, j] < 0) { box.Text = ((-1) * grid.Grid[i, j]).ToString(); box.Style = (Style)(Resources["GridElementDisabled"]); } else if (grid.Grid[i, j] > 0) { box.Text = grid.Grid[i, j].ToString(); } else { box.Text = ""; } } } } }
/// <summary> /// FindFewestChoices finds the first cell having the smallest number /// of available choices, and sets the row and column of that cell for /// use in SolveGrid /// </summary> /// <param name="grid">Current state of the grid</param> /// <param name="r">OUTPUT sets r for use in caller</param> /// <param name="c">OUTPUT sets c for use in caller</param> /// <param name="numChoices">OUTPUT sets var for use in caller</param> /// <returns>Returns true if valid cell found, false if not</returns> private bool FindFewestChoices(PuzzleGrid grid, out int r, out int c, out int numChoices) { bool[] minList = new bool[10]; int numCh, minR, minC, minChoice, i, j; bool bad, result; minChoice = 10; minR = 0; minC = 0; for (i = 1; i < 10; i++) //Initialize minList to FALSE { minList[i] = false; } bad = false; i = 0; while (!bad && i < 9) //While not a bad solutn and trying valid row { j = 0; while (!bad && j < 9) //not a bad solutn and trying valid column { if (grid.Grid[i, j] == 0) { numCh = ListPossible(i, j, grid); //Get # of choices if (numCh == 0) //If no choices found, bad solution { bad = true; } else //If not bad solutn... { if (numCh < minChoice) //If less than current min { minChoice = numCh; //Set new min value list.CopyTo(minList, 0);//Save list of possible minR = i; //set row of cell with least minC = j; //set col of cell with least } } } j++; } i++; } if (bad || minChoice == 10) //If bad solutn or minChoice never set { result = false; //No fewest possible choices r = 0; c = 0; numChoices = 0; } else { result = true; //Valid cell found, return information to caller r = minR; c = minC; numChoices = minChoice; minList.CopyTo(list, 0); } return result; }
public PuzzleGrid InitGrid() { //Randomly fill in the first row and column of puzzlegrid PuzzleGrid tempGrid = new PuzzleGrid { }; //temporary grid to assign values into int row = 0; //variable for navigating 'rows' int col = 0; //variable for navigating 'columns' int newVal; //value to place into grid bool solved; List <int> valueSet = new List <int>(Enumerable.Range(-9, 9)); //range //of numbers that can be added to the grid List <int> valueSet2 = new List <int>(); //placeholder values in column 0 Random rnd = new Random(); //random variable for choosing random number int randIndex = 0; //index in valueSet/valueSet2 that is accessed randIndex = rnd.Next(0, 8); //get a random number and place in grid(0,0) newVal = valueSet[randIndex]; tempGrid.InitSetCell(row, col, newVal); valueSet.Remove(newVal); //remove paced value from options for (row = 1; row < 9; row++) { //fills in column 0 with remaining possible values, storing in place- //holder as it goes so as to preserve when placing in row 0 later randIndex = rnd.Next(0, valueSet.Count); newVal = valueSet[randIndex]; valueSet2.Add(newVal); valueSet.Remove(newVal); tempGrid.InitSetCell(row, col, newVal); } row = 0; //reset row to 0 for (col = 1; col < 3; col++) { //fills in col 1,2 of row 0, checking that don't duplicate the //values in rows 1,2 of col 0 randIndex = rnd.Next(0, valueSet2.Count); newVal = valueSet2[randIndex]; while ((newVal == tempGrid.Grid[1, 0] || (newVal == tempGrid.Grid[2, 0]))) { randIndex = rnd.Next(0, valueSet2.Count); newVal = valueSet2[randIndex]; } valueSet2.Remove(newVal); tempGrid.InitSetCell(row, col, newVal); } for (col = 3; col < 9; col++) { //fill in remainder of row 0 with remaining possible values randIndex = rnd.Next(0, valueSet2.Count); newVal = valueSet2[randIndex]; valueSet2.Remove(newVal); tempGrid.InitSetCell(row, col, newVal); } do { puzzleSolver = new PuzzleSolver(); puzzleSolver.SolveGrid((PuzzleGrid)tempGrid.Clone(), false); //Slv to fill remainder of grid SolutionGrid = puzzleSolver.SolutionGrid; } while (SolutionGrid == null || SolutionGrid.IsBlank()); PermaGrid = Blanker(SolutionGrid); //call Blanker to carry out the return(PermaGrid); //blanking of fileds,then return the grid to user to solve }
/// <summary> /// IsInColumn checks if given value occurs in the given row. /// </summary> /// <param name="grid">Current state of the grid</param> /// <param name="col">Column being check</param> /// <param name="value">Value being sought</param> /// <returns></returns> private bool IsInCol(PuzzleGrid grid, int col, int value) { bool result = false; for (int i = 0; i < 9; i++) //Iterate through column { //check if cell holds value being sought result = result || (Math.Abs(grid.Grid[i, col]) == value); } return result; }
/// <summary> /// IsInRow checks if given value occurs in the given row /// </summary> /// <param name="grid">Current state of puzzle grid</param> /// <param name="row">Row to check</param> /// <param name="value">Value to look for</param> /// <returns></returns> private bool IsInRow(PuzzleGrid grid, int row, int value) { bool result = false; for (int i = 0; i < 9; i++) //Iterate through row { //check if cell holds value being sought result = result || (Math.Abs(grid.Grid[row, i]) == value); } return result; }
// Call SolveGrid to solve puzzlegrid //Store solved gamegrid as the correct solution in solutiongrid public PuzzleGrid Blanker(PuzzleGrid solvedGrid) { //enable blanking of squares based on difficulty PuzzleGrid tempGrid; PuzzleGrid saveCopy; //temporary grids to save between tests bool unique = true; //flag for if blanked form has unique soln int totalBlanks = 0; //count of current blanks int tries = 0; //count of tries to blank appropriately int desiredBlanks; //amount of blanks desired via difficulty int symmetry = 0; //symmetry type tempGrid = (PuzzleGrid)solvedGrid.Clone(); //cloned input grid (no damage) Random rnd = new Random(); //allow for random number generation switch (difficulty) //set desiredBlanks via chosen difficulty { case Difficulty.Easy: //easy difficulty desiredBlanks = 40; break; case Difficulty.Medium: //medium difficulty desiredBlanks = 45; break; case Difficulty.Hard: //hard difficulty desiredBlanks = 50; break; default: //easy difficulty desiredBlanks = 40; break; } symmetry = rnd.Next(0, 2); //Randomly select symmetry do { //call RandomlyBlank() to blank random squares symmetrically saveCopy = (PuzzleGrid)tempGrid.Clone(); // in case undo needed tempGrid = RandomlyBlank(tempGrid, symmetry, ref totalBlanks); //blanks 1 or 2 squares according to symmetry chosen puzzleSolver = new PuzzleSolver(); unique = puzzleSolver.SolveGrid((PuzzleGrid)tempGrid.Clone(), true); // will it solve uniquely? if (!unique) { tempGrid = (PuzzleGrid)saveCopy.Clone(); tries++; } } while((totalBlanks < desiredBlanks) && (tries < 1000)); solvedGrid = tempGrid; solvedGrid.Finish(); return(solvedGrid); }
public PuzzleGrid RandomlyBlank(PuzzleGrid tempGrid, int sym, ref int blankCount) { //blank one or two squares(depending on if on center line) randomly Random rnd = new Random(); //allow random number generation int row = rnd.Next(0, 8); //choose randomly the row int column = rnd.Next(0, 8); //and column of cell to blank while (tempGrid.Grid[row, column] == 0) //don't blank a blank cell { row = rnd.Next(0, 8); column = rnd.Next(0, 8); } tempGrid.InitSetCell(row, column, 0); //clear chosen cell blankCount++; //increment the count of blanks switch (sym) { //based on symmetry, blank a second cell case 0: //vertical symmetry if (tempGrid.Grid[row, 8 - column] != 0) //if not already blanked { blankCount++; //increment blank counter } tempGrid.InitSetCell(row, 8 - column, 0); //blank opposite cell break; case 1: //horizontal symmetry if (tempGrid.Grid[8 - row, column] != 0) { blankCount++; } tempGrid.InitSetCell(8 - row, column, 0); break; case 2: //diagonal symmetry if (tempGrid.Grid[column, row] != 0) { blankCount++; } tempGrid.InitSetCell(column, row, 0); break; default: //diagonal symmetry if (tempGrid.Grid[row, 8 - column] != 0) { blankCount++; } tempGrid.InitSetCell(column, row, 0); break; } return(tempGrid); }
/// <summary> /// This clones the object. /// </summary> /// <returns>A clone of itself.</returns> public object Clone() { //enable cloning for safe copying of the object PuzzleGrid p = new PuzzleGrid(); for (int i = 0; i < Max; i++) { for (int j = 0; j < Max; j++) { p.InitSetCell(i, j, Grid[i, j]); } } return(p); }
// The following methods are helper methods /// <summary> /// Enters the puzzle entry state. /// </summary> private void EnterPuzzleEntryState() { // clear puzzle grid puzzleGrid = new PuzzleGrid(); SetPuzzleGrid(puzzleGrid); // hide buttons SolveNow.Visibility = Visibility.Hidden; HintsBox.Visibility = Visibility.Hidden; HintsBoxTitle.Visibility = Visibility.Hidden; // show correct buttons EntryComplete.Visibility = Visibility.Visible; inPuzzleEnterMode = true; }
/// <summary> /// IsSolved checks to see if all cells in the grid contain a value. /// If so, due to how the solve algorithm solves, the puzzle must be /// solved correctly. /// </summary> /// <param name="grid">Current state of the puzzle grid</param> /// <returns>TRUE: Puzzle is solved, FALSE: Not solved</returns> private bool IsSolved(PuzzleGrid grid) { bool result = true; //Assume puzzle is solved int r, c; r = 0; while (result == true && r < 9) //Check every row { c = 0; while (result == true && c < 9) //Check every column { //If an empty cell is found, result gets FALSE result = (result && grid.Grid[r, c] != 0); c++; } r++; } return result; }
/// <summary> /// ListPossible populates list[] with "true" at each index /// representing that value is a possible value for cell at [row, col]. /// It returns the total count of possible values in that square /// </summary> /// <param name="row">Row of target cell</param> /// <param name="col">Column of target cell</param> /// <param name="g">current state of grid</param> /// <returns>Integer count of possible values for given cell</returns> private int ListPossible(int row, int col, PuzzleGrid g) { int count = 0; ClearList(); //Create a fresh list for bool population for (int i = 1; i < 10; i++) //For i = 1..9 (valid values) { if (IsPossible(g, row, col, i) == true) //If i can go in cell { list[i] = true; count++; } else //Value of i found in Row, Col, or 3x3 { list[i] = false; } } return count; }
/// <summary> /// Creates a new game. /// </summary> private void MakeNewGame(bool noCancel = false) { var dlg = new NewGameDifficultyDialogBox(noCancel) { Owner = this }; dlg.ShowDialog(); var difficulty = dlg.HowHard; if (dlg.CreateGame) { var puzzleGenerator = new PuzzleGenerator(difficulty); PuzzleGrid newPuzzleGrid = puzzleGenerator.InitGrid(); SetPuzzleGrid(newPuzzleGrid); puzzleGrid = newPuzzleGrid; } }
/// <summary> /// IsIn3x3 determines which 3x3 grid cell is in using GroupNum. /// Values are: /// [0, 0] [0, 1] [0, 2] /// [1, 0] [1, 1] [1, 2] /// [2, 0] [2, 1] [2, 2] /// And then checks if given value occurs in the 3x3 grid. /// </summary> /// <param name="g">Current state of the grid</param> /// <param name="row">Row of cell being checked</param> /// <param name="col">Column of cell being checked</param> /// <param name="value">Value being sought</param> /// <returns>True if Value is found, false if not</returns> private bool IsIn3X3(PuzzleGrid g, int row, int col, int value) { int rLow; int cLow; rLow = 3 * GroupNum(row); //Index of smallest number row in grid cLow = 3 * GroupNum(col);//Index of smallest number columin in grid bool result = false; for (int i = rLow; i < rLow + 3; i++) //Check all 3 rows in subgrid { for (int j = cLow; j < cLow + 3; j++) //Check all 3 columns { //Compare value of cell with value being sought if (Math.Abs(g.Grid[i, j]) == value) { result = true; } } } return result; }
/// <summary> /// This function opens a game. /// </summary> public PuzzleGrid OpenFile(String fileName) { PuzzleGrid openedPuzzle = new PuzzleGrid(); int i = 0; //mnemonic: row int j = 0; //mnemonic: column int cellVal = 0; bool eOF = false; using (StreamReader reader = new StreamReader(fileName)) { for (i = 0; i < 9; i++) { string currentLine; if ((currentLine = reader.ReadLine()) != null) { for (j = 0; j < 9; j++) { cellVal = (int)currentLine[2 * j] - '0'; if (currentLine[(2 * j) + 1] == '+') { openedPuzzle.InitSetCell(i, j, cellVal); } else if (currentLine[(2 * j) + 1] == '-') { openedPuzzle.InitSetCell(i, j, -(cellVal)); } } } else { eOF = true; } } } if (eOF) { return(null); } else { return(openedPuzzle); } }
/// <summary> /// This function saves a game. /// </summary> public bool SaveFile(PuzzleGrid board, String fileName, bool unsolved = false) { int i = 0; //mnemonic: row int j = 0; //mnemonic: column TextWriter writer = new StreamWriter(fileName); int cellVal; for (i = 0; i < 9; i++) { string currentLine = ""; for (j = 0; j < 9; j++) { cellVal = board.Grid[i, j]; if (cellVal < 0) { currentLine += Math.Abs(cellVal).ToString(); currentLine += "-"; } else { if (unsolved) { currentLine += "0"; } else { currentLine += cellVal.ToString(); } currentLine += "+"; } } writer.WriteLine(currentLine); } writer.Close(); return(true); }
/// <summary> /// This method shows the open file dialog box. /// </summary> private void OpenFile() { // Show save file dialog box var result = openGameDialog.ShowDialog(); // Process save file dialog box results if (result == true) { // Save document string fileName = openGameDialog.FileName; PuzzleGrid grid = fileHandler.OpenFile(fileName); puzzleGrid = (PuzzleGrid)grid.Clone(); if (grid == null) { MessageBox.Show("There was a problem opening your file. Please try again."); } else { SetPuzzleGrid(grid); } } }
/// <summary> /// This function instantiates all the game objects and sets up the game window. /// </summary> public UserInterface() { InitializeComponent(); // Initialize game elements fileHandler = new FileHandler(); //puzzleSolver = new PuzzleSolver(); //var result = puzzleSolver.SolveGrid(puzzleGrid, true); //StatusBarText.Text = result ? "This puzzle has a solution solved." : "This puzzle cannot be solved."; // Initialize the save game dialog saveGameDialog = new SaveFileDialog { Filter = "Sudoku Games | *.sud", DefaultExt = ".sud" }; openGameDialog = new OpenFileDialog { Filter = "Sudoku Games | *.sud", DefaultExt = ".sud" }; puzzleGrid = new PuzzleGrid(); SetPuzzleGrid(puzzleGrid); SetHintsBoxToDefault(); }
/// <summary> /// SolveGrid attempts to solve a puzzle by checking through all /// possible values for each cell, discarding values that do not lead /// to a valid solution. On a try, it recursively calls itself to /// maintain previous states of the grid to back track to if the /// current path fails. It creates a local version of the grid to /// facilitate this. It also checks if the puzzle is uniquely solvable. /// </summary> /// <param name="g">Current state of the grid</param> /// <param name="checkUnique">Do we care if it has unique soln?</param> /// <returns></returns> public bool SolveGrid(PuzzleGrid g, bool checkUnique) { PuzzleGrid grid = new PuzzleGrid(); grid = (PuzzleGrid)g.Clone(); //Copy the input grid int i, choice, r, c, numChoices; bool done, got_one, solved, result; got_one = false; recursions++; FillSingleChoices(grid); //First, fill in all single choice values if (IsSolved(grid)) //If it's already solved { if (numSolns > 0) //If another soln already found { stoplooking = true; //Don't look for more result = false; //Return false, no UNIQUE soln } else //If no other soln found yet { numSolns++; final[numSolns] = (PuzzleGrid)g.Clone(); //Save found soln result = true; SolutionGrid = grid; } } else //If not solved yet { if (!FindFewestChoices(grid, out r, out c, out numChoices)) { result = false; //Invalid solution } else //Current grid still valid { i = 1; done = false; got_one = false; while (!done && i <= numChoices) { choice = PickOneTrue(); //Pick a possible value list[choice] = false; //Won't want to use it again grid.UserSetCell(r, c, choice); if (recursions < MaxDepth) { //-----------We must go deeper. SUDCEPTION!-----------// solved = (SolveGrid(grid, checkUnique)); //Recurse } else { solved = false; } if (stoplooking == true) { done = true; got_one = true; } else { got_one = (got_one || solved); if (!checkUnique) //If not looking for unique soln { done = got_one; //Then we have a solution } } i++; } result = got_one; } } return result; }