/// <summary> /// When the File -> Save Game Unsolved menu item is chosen /// </summary> private void FileSaveGameUnsolvedClick(object sender, EventArgs e) { Grid unsolvedGrid = grid.Copy(); unsolvedGrid.ForEachSquare((row, col, val) => { if (unsolvedGrid.IsEditable(row, col)) { unsolvedGrid.Clear(row, col); } }); GameManager.SaveGame(unsolvedGrid); }
/// <summary> /// Helper function: count the blank squares in the given grid. /// </summary> /// <returns>the number of blank squares</returns> private int CountBlank(Grid grid) { int nBlanks = 0; grid.ForEachSquare((row, col, val) => { if (val == 0) { nBlanks += 1; } }); return(nBlanks); }
/// <summary> /// When the player transitions from "edit grid" mode to "play this game" mode /// </summary> internal static void PlayThisPuzzle(Grid oldGrid, Form form) { // Set all the squares as non-editable Grid grid = oldGrid.Copy(); grid.ForEachSquare((row, col, val) => { if (val != 0) { grid.SetEditable(false, row, col); } }); // Tries to solve the grid. If the grid has no unique solution, warn first. Grid copyGrid = grid.Copy(); // yes another copy; the solver will mess with this one. bool result = (new Solver().Solve(copyGrid)); if (result) { SaveGame(grid); form.Hide(); GameForm gform = new GameForm(grid, true); gform.ShowDialog(); form.Close(); } else { if (MessageBox.Show(copyGrid.IsFull()? "This puzzle can be solved in more than one way. Play anyway?" : "This puzzle cannot be solved. Play anyway?", "Unsolvable Puzzle", MessageBoxButtons.YesNo) == DialogResult.Yes) { SaveGame(grid); form.Hide(); GameForm gform = new GameForm(grid, true); gform.ShowDialog(); form.Close(); } } }
/// <summary> /// Helper function: count the blank squares in the given grid. /// </summary> /// <returns>the number of blank squares</returns> private int CountBlank(Grid grid) { int nBlanks = 0; grid.ForEachSquare((row, col, val) => { if (val == 0) { nBlanks+=1; } }); return nBlanks; }
/// <summary> /// Generates a grid with the given difficulty level. /// </summary> public Grid Generate(DifficultyLevel difficulty) { // Generates stuff // 1. Randomnly fill in the top and left part of the grid. // 2. Try to solve it // 3. Until we have enough blanks: // remove some blanks // if it isn't uniquely solvable, add those two blanks again. Grid grid = GenerateBlankGrid(); Grid result; // Top row List <int> row = Enumerable.Range(1, 9).OrderBy((i) => rand.Next()).ToList(); for (int i = 0; i < 9; i++) { grid.Set(row[i], false, i, 0); } // Top column row = Enumerable.Range(1, 9).OrderBy((i) => rand.Next()).ToList(); row.Remove(grid.Get(0, 0)); for (int i = 0; i < 8; i++) { grid.Set(row[i], false, 0, i + 1); } if (solver.FindErrors(grid).Count > 0) { // This is not enough to guarantee correctness. // If we get an invalid grid, try try again! result = Generate(difficulty); } else { solver.Solve(grid); // How many blanks do we need? int targetBlanks = 0; switch (difficulty) { case DifficultyLevel.Easy: targetBlanks = 30; break; case DifficultyLevel.Medium: targetBlanks = 45; break; case DifficultyLevel.Hard: targetBlanks = 50; break; } // Remove squares until we have the right number of blanks. int tries = 0; while (tries < 100 && CountBlank(grid) < targetBlanks) { Grid saveCopy = grid.Copy(); // Solving is expensive. Blanking squares is easy! // When the grid is mostly full, you can blank squares // in relative safety without generating a non-unique // puzzle. When the puzzle gets more sparse, you // need to be more careful. // That's what we're doing here. Quick optimization. for (int i = 0; i < (targetBlanks - CountBlank(grid)) / 2 + 1; i++) { MaybeRandomBlank(grid); } if (!solver.Solve(grid.Copy())) { // it failed grid = saveCopy; } tries++; } //Console.WriteLine("Generated puzzle in " + tries + " tries with "+CountBlank(grid)+" blanks"); // Finally, set every square to be not editable grid.ForEachSquare((r, c, val) => { if (val != 0) { grid.SetEditable(false, r, c); } }); result = grid; } return(result); }