Пример #1
0
 /// <summary>
 /// Takes as input a 9x9 integer grid array and a file name,
 /// and returns true if successful in writing the named file.
 /// </summary>
 public bool WriteFile(Grid grid, string fileName)
 {
     try
     {
         // Write the string to a file.
         System.IO.StreamWriter file = new System.IO.StreamWriter(fileName);
         for (int i = 1; i < 10; i++)
         {
             string line = "";
             for (int j = 1; j < 10; j++)
             {
                 line += Math.Abs(grid.Get(i-1, j-1));
                 if (grid.IsEditable(i - 1, j - 1))
                 {
                     line += '+';
                 }
                 else{
                     line += '-';
                 }
             }
             file.WriteLine(line);
         }
         file.Close();
         return true;
     }
     catch (Exception e)
     {
         // Let the user know what went wrong.
         MessageBox.Show("The file could not be written: '"+e.Message+"'");
         return false;
     }
 }
Пример #2
0
 /// <summary>
 /// Takes as input a 9x9 integer grid array and a file name,
 /// and returns true if successful in reading the named file.
 /// </summary>
 public bool ReadFile(Grid grid, string fileName)
 {
     try
     {
         // Create an instance of StreamReader to read from a file.
         // The using statement also closes the StreamReader.
         using (StreamReader sr = new StreamReader(fileName))
         {
             String line;
             // Read and display lines from the file until the end of
             // the file is reached.
             for (int i=0;i<9;i++)
             {
                 line= sr.ReadLine();
                 for (int j = 1; j < 10; j++)
                 {
                     if (line[2 * (j - 1) + 1] == '-')
                     {
                         grid.Set((0-(line[2 * (j - 1)] - 48)), false, i, j-1);
                     }
                     else {
                         grid.Set(line[2 * (j - 1)] - 48, true, i, j-1);
                     }
                 }
             }
         }
         return true;
     }
     catch (Exception e)
     {
         // Let the user know what went wrong.
         MessageBox.Show("The file could not be read: '"+e.Message+"'");
         return false;
     }
 }
        /// <summary>
        /// This control is a sudoku grid control. It displays a sudoku grid
        /// in an attractive way.
        /// </summary>
        public SudokuGridControl(Grid bgrid)
        {
            this.Resize += new EventHandler(FixFontsAndSize);
            this.ResizeRedraw = false;
            InitializeComponent();

            this.grid = bgrid;

            ResetTextboxen();
            UpdateGridView();

            FixFontsAndSize(new Object(), new EventArgs());
        }
Пример #4
0
        /// <summary>
        /// Shows a dialog for saving the grid.
        /// </summary>
        /// <param name="grid">The grid to be saved</param>
        /// <returns>Whether the save succeeded. False if the user canceled.</returns>
        public static bool SaveGame(Grid grid)
        {
            bool result = false;
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "Sudoku Game|*.sud";
            dialog.Title = "Save Game";
            dialog.ShowDialog();

            if (dialog.FileName != "")
            {
                new File().WriteFile(grid, dialog.FileName);
                result = true;
            }
            return result;
        }
Пример #5
0
        /// <summary>
        /// Loads a game from disk (showing load dialog) and then shows the game form.
        /// </summary>
        /// <param name="form">This form will be hidden.</param>
        public static bool LoadGame(Form form)
        {
            bool result = false;
            File fileOpener = new File();
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "Sudoku Game|*.sud";
            dialog.Title = "Load Game";
            dialog.ShowDialog();

            if (dialog.FileName != "")
            {
                form.Hide();
                Grid newGrid = new Grid();
                fileOpener.ReadFile(newGrid, dialog.FileName);
                GameForm gform = new GameForm(newGrid, true);
                gform.ShowDialog();
                form.Close();
                result = true;
            }
            return result;
        }
Пример #6
0
 /// <summary>
 /// The actual meat of the depthfirst search goes here.
 /// (solve is mostly bookkeeping)
 /// </summary>
 /// <param name="eachSolutionAction">
 /// This lambda will be run once for each solution.
 /// </param>
 /// <param name="grid">
 /// This grid will be left in an undefined state
 /// after DepthFirstSearch() terminates.
 /// If you want a good solution, be sure to save
 /// a copy in your eachSolutionAction.
 /// </param>
 public void DepthFirstSearch(Grid grid, Action eachSolutionAction)
 {
     CellConsideration cell = Consider(grid);
     if (grid.IsFull())
     {
         // Found a solution!
         eachSolutionAction();
     }
     else if (cell != null)
     {
         foreach (int hint in cell.PossibleValues)
         {
             grid.Set(hint, true, cell.Row, cell.Col);
             DepthFirstSearch(grid, eachSolutionAction);
             grid.Set(0, true, cell.Row, cell.Col);
             // speed hack: we know the previous square
             // was a zero, so no need to save a copy
             // of the grid every time.
         }
     }
 }
        /// <summary>
        /// This method creates three new sudoku grids.
        /// </summary>
        private void makeNewGrids()
        {
            easy = gen.Generate(DifficultyLevel.Easy);
            easyPanel.Controls.Clear();
            SudokuGridControl easyGcontrol = new SudokuGridControl(easy);
            easyPanel.Controls.Add(easyGcontrol);
            easyGcontrol.Dock = DockStyle.Fill;
            easyGcontrol.IsEditable = false;

            med = gen.Generate(DifficultyLevel.Medium);
            medPanel.Controls.Clear();
            SudokuGridControl medGcontrol = new SudokuGridControl(med);
            medPanel.Controls.Add(medGcontrol);
            medGcontrol.Dock = DockStyle.Fill;
            medGcontrol.IsEditable = false;

            hard = gen.Generate(DifficultyLevel.Hard);
            hardPanel.Controls.Clear();
            SudokuGridControl hardGcontrol = new SudokuGridControl(hard);
            hardPanel.Controls.Add(hardGcontrol);
            hardGcontrol.Dock = DockStyle.Fill;
            hardGcontrol.IsEditable = false;
        }
 private void medButtonClick(object sender, EventArgs e)
 {
     result = med;
     hasResult = true;
     this.Close();
 }
 private void hardButton_Click(object sender, EventArgs e)
 {
     result = hard;
     hasResult = true;
     this.Close();
 }
Пример #10
0
 /// <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;
 }
Пример #11
0
 /// <summary>
 /// Considers each cell of the grid.
 /// </summary>
 /// <returns>
 /// Returns the cell with the smallest number
 /// of possible values. This square is a good
 /// place to continue search.
 /// Returns null if the grid is invalid.
 /// </returns>
 private CellConsideration Consider(Grid grid)
 {
     int smallestNOfHints = int.MaxValue;
     CellConsideration smallestConsideration = null;
     for (int row = 0; row < 9; row++)
     {
         for (int col = 0; col < 9; col++)
         {
             if (grid.Get(row, col) == 0)
             {
                 List<int> hints = GetHintsFor(grid, row, col);
                 if (hints.Count == 0)
                 {
                     // We found a square that has no solution
                     // This means the current state of the puzzle is bogus
                     // so stop.
                     return null;
                 }
                 if (hints.Count < smallestNOfHints)
                 {
                     smallestConsideration = new CellConsideration(row, col, hints);
                     smallestNOfHints = hints.Count;
                 }
             }
         }
     }
     return smallestConsideration;
 }
Пример #12
0
        /// <summary>
        /// Solves the grid in-place.
        /// </summary>
        /// <param name="grid">
        /// The grid to solve. This grid will be modified to contain the solution to
        /// the sudoku puzzle, if it exists.
        /// </param>
        /// <returns>
        /// If the puzzle can be unambiguously solved, Solve() will return true.
        /// If the puzzle cannot be solved or has more than one solution,
        /// this will return false.
        /// </returns>
        public bool Solve(Grid grid)
        {
            int numSolutions = 0;
            Grid solutionGrid = grid.Copy();
            if (FindErrors(grid).Count > 0)
            {
                // fail fast!
                return false;
            }

            // this bit here GREATLY simplifies DepthFirstSearch.
            // Using exceptions and a lambda to terminate after two
            // solutions frees DepthFirstSearch from worrying about
            // all that logic at the cost of a closure and tricky
            // exception handling.
            // all of this is to avoid C#'s lack of generators, buh
            try {
                DepthFirstSearch(grid, () => {
                    // this lambda gets run on each solution
                    numSolutions++;
                    solutionGrid = grid.Copy();
                    if (numSolutions > 1) {
                        throw new StopIterationException();
                    }
                });
            }
            catch (StopIterationException e) { }
            // (just pretend that empty catch block is a generator)

            grid.CopyFrom(solutionGrid);
            return numSolutions == 1;
        }
Пример #13
0
 /// <summary>
 /// Returns true if the puzzle isn't obviously broken.
 /// </summary>
 public bool IsValidPuzzle(Grid grid)
 {
     return FindErrors(grid).Count == 0;
 }
Пример #14
0
 /// <summary>
 /// Get the list of valid moves for the given cell in the given grid.
 /// Used by the Hints bar and Consider()
 /// </summary>
 /// <returns>
 /// A list of valid move choices.
 /// </returns>
 public List<int> GetHintsFor(Grid grid, int row, int col)
 {
     // believe it or not, Lists are faster than int[]s for some reason
     List<int> stuff = grid.GetColumn(col).Concat(grid.GetRow(row)).Concat(grid.GetSquareAbout(row, col)).ToList();
     List<int> results = new List<int>();
     for (int i = 1; i <= 9; i++)
     {
         if (!stuff.Contains(i))
         {
             results.Add(i);
         }
     }
     return results;
 }
Пример #15
0
        /// <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();
                }
            }
        }
Пример #16
0
 /// <summary>
 /// Finds errors in the grid. Returns a list of cells such that:
 ///     - there is another cell with the same value in the row
 ///     - there is another cell with the same value in the column
 ///     - there is another cell with the same value in the 3x3 square
 ///       where the cell resides
 /// </summary>
 public List<List<int>> FindErrors(Grid grid)
 {
     List<List<int>> errors = new List<List<int>>();
     for (int row = 0; row < 9; row++)
     {
         for (int col = 0; col < 9; col++)
         {
             int val = grid.Get(row, col);
             // The same cell in the row?
             bool valueInRow = grid.GetRow(row).Count((eachCell) => eachCell == val) > 1;
             // The same cell in the column?
             bool valueInCol = grid.GetColumn(col).Count((eachCell) => eachCell == val) > 1;
             // The same cell in the 3x3 square?
             bool valueInSquare = grid.GetSquareAbout(row, col).Count((eachCell) => eachCell == val) > 1;
             if (val != 0 && (valueInRow || valueInCol || valueInSquare))
             {
                 errors.Add(new List<int>(new int[] { row, col }));
             }
         }
     }
     return errors;
 }
Пример #17
0
 /// <summary>
 /// Overwrite this grid's elements with elements from another grid.
 /// The two grids will share nothing.
 /// </summary>
 public void CopyFrom(Grid other)
 {
     this.elts = other.Copy().elts;
 }
Пример #18
0
 private void easyButton_Click(object sender, EventArgs e)
 {
     result = easy;
     hasResult = true;
     this.Close();
 }
Пример #19
0
        /// <summary>
        /// Blank two squares in a symmetric fashion, or not.
        /// 
        /// We always use rotational symmetry because it's prettiest.
        /// </summary>
        private void MaybeRandomBlank(Grid grid)
        {
            int row1 = rand.Next(9);
            int col1 = rand.Next(9);

            int row2 = (8 - row1);
            int col2 = (8 - col1);

            if (grid.Get(row1, col1) != 0 && grid.Get(row2, col2) != 0)
            {
                grid.Set(0, true, row1, col1);
                grid.Set(0, true, row2, col2);
            }
        }
Пример #20
0
        /// <summary>
        /// Creates a new form for playing the game
        /// </summary>
        /// <param name="grid">The grid to start with</param>
        /// <param name="playingMode">If true: We're "playing" the game. If false: We're "editing" the game.</param>
        public GameForm(Grid grid, bool playingMode)
        {
            InitializeComponent();
            this.grid = grid;
            this.isPlaying = playingMode;

            hintBarText.Text = "";
            if (isPlaying)
            {
                solveButton.Text = "&Solve";
            }
            else
            {
                solveButton.Text = "&Enter Puzzle";
            }

            // make the grid control and put it somewhere
            this.gcontrol = new SudokuGridControl(grid);
            this.gridPanel.Controls.Add(gcontrol);
            gcontrol.Dock = DockStyle.Fill;

            // When the cell is cleared, mark/unmark errors and hints
            gcontrol.CellClear += (int row, int col) =>
            {
                nagAboutErrors = true;
                nagAboutWonGame = true;
                RecalculateErrors();
                RecalculateHints(row, col);
                ShowOrHideHintBar();
            };

            // When the cell's value is changed, mark/unmark errors and hints
            gcontrol.CellChange += (int row, int col) =>
            {
                MaybeTryGameOver();
                RecalculateErrors();
                RecalculateHints(row, col);
                ShowOrHideHintBar();
            };

            // When the user selects a different cell, mark/unmark errors and hints
            gcontrol.CellFocused += (int row, int col) =>
            {
                ShowOrHideHintBar();
                if (isPlaying)
                {
                    RecalculateHints(row, col);
                }
            };

            // Add a drop-down context menu to each textbox
            gcontrol.ForEachTextBox((TextBox tbox, int row, int col) =>
            {
                tbox.ContextMenu = new ContextMenu();
                if (isPlaying)
                {
                    // The context menu should only appear when in "Playing" mode
                    tbox.ContextMenu.MenuItems.Add(new MenuItem("Show &Hints", (s, e) =>
                        {
                            hintBarText.Show();
                            // the hints bar will disappear on next call to ShowOrHideHints()
                        }));
                    tbox.ContextMenu.MenuItems.Add(new MenuItem("&Solve This Square", (s, e) =>
                        {
                            // solve the grid and copy the value
                            if (grid.IsEditable(row, col))
                            {
                                Grid solvedGrid = grid.Copy();
                                solver.Solve(solvedGrid);
                                if (!solvedGrid.IsFull())
                                {
                                    MessageBox.Show("This square cannot be solved because there are errors in your puzzle. Please erase some of your work and try again.");
                                }
                                else
                                {
                                    grid.Set(solvedGrid.Get(row, col), true, row, col);
                                }
                                gcontrol.UpdateGridView();
                                RecalculateErrors();
                            }
                        }));
                }
            });

            // initial setup: start the timer and optionally show errors.
            gameTimerTick(this, new EventArgs());
            RecalculateErrors();
            ShowOrHideHintBar();
        }