Upload
vudung
View
220
Download
0
Embed Size (px)
Citation preview
Sudoku Solvers Man versus Machine
Simon Johansson Andreas Broström G.a. Huddingevägen 438 Norrtullsgatan 33 125 42 Älvsjö 113 27 Stockholm 0704-74 54 84 0707-90 07 14 [email protected] [email protected] Degree Project in Computer Science, First Level, DD143X School of Computer Science and Communication KTH Royal Institute of Technology Supervisor: Roberto Bresin Examiner: Mårten Björkman
3
Abstract The purpose of this bachelor thesis is to create and implement a Sudoku solver based on human strategies. We want to see how our solver compares to a brute-force solver and in which aspects it is the most efficient. We tested the two solvers in terms of solving speed, number of misses or wrong moves required to find a solution, and ability to solve harder Sudokus. Our conclusions are that the human solver is comparable to well known computer based Sudoku solvers and it is generally more efficient in terms of solving speed and number of misses. The human solver, however, does not guarantee solutions to advanced Sudokus, even though it solved all puzzles that matched the complexity of Sudokus found in newspapers.
Sudokulösare
Människa mot Maskin
Sammanfattning Syftet med den här kandidatuppsatsen är att skapa och implementera en sudokulösare baserat på mänskliga strategier. Vi vill se hur vår lösare står sig mot en brute-force lösare och i vilka avseenden den kan anses vara mest effektiv. Vi har jämfört de två lösarnas lösningshastighet, antal missar eller felaktiga beräkningssteg och förmågan att lösa svårare sudokus. Våra slutsatser är att den mänskliga lösaren är jämförbar med välkända datorbaserade lösare och den är generellt sett effektivare när det gäller lösningshastighet och antal missar. Den mänskliga lösaren kan dock inte garantera en lösning till mer avancerade sudokus, även fast den löste alla de pussel som hade en motsvarande svårighetsgrad som de man kan hitta i tidningen.
4
Statement of collaboration The responsibilities are divided into three subsections: Implementation of the solvers, testing environment and execution, and writing. Implementation of the solvers Simon has been responsible for the human solver and Andreas the brute-force solver. Both of us have worked on the basic classes of which both solvers inherit from. Testing environment and execution Andreas has been responsible for setting up the testing environment and writing the code needed to perform the tests. Writing Both of us have in general been doing the writing together but some of the more individual responsibilities are: Simon: Background of techniques, general approach, approach of implementations excluding brute-force and results. Andreas: General background, background of algorithms, approach of brute-force and testing.
5
Table of contents
STATEMENT OF COLLABORATION ................................................................................................................. 4 TERMINOLOGY ...................................................................................................................................................... 7 INTRODUCTION .................................................................................................................................................... 8 PREFACE ...................................................................................................................................................................................... 8
Sudoku ...................................................................................................................................................................................................................... 8 Sudoku Solvers ...................................................................................................................................................................................................... 8 Solving techniques and algorithms .............................................................................................................................................................. 9
PROBLEM STATEMENT ............................................................................................................................................................. 9 PURPOSE ..................................................................................................................................................................................... 9 OUTLINE ................................................................................................................................................................................... 10
BACKGROUND .................................................................................................................................................... 11 SUDOKU .................................................................................................................................................................................... 11 SOLVING SUDOKUS ................................................................................................................................................................. 11 Algorithms ............................................................................................................................................................................. 11
Brute-‐force algorithm ..................................................................................................................................................................................... 12 Human algorithm .............................................................................................................................................................................................. 12
Techniques ............................................................................................................................................................................ 12 Naked Single ........................................................................................................................................................................................................ 13 Hidden Single ...................................................................................................................................................................................................... 13 Naked Pair/Triple ............................................................................................................................................................................................. 13 Hidden Pair/Triple ........................................................................................................................................................................................... 13 Naked Quad .......................................................................................................................................................................................................... 13 Box Line Reduction ........................................................................................................................................................................................... 13 Pointing Pairs/Triples ..................................................................................................................................................................................... 13
APPROACH ........................................................................................................................................................... 14 IMPLEMENTATION .................................................................................................................................................................. 14 Human rule based solver ................................................................................................................................................ 15 Brute-‐force solver .............................................................................................................................................................. 15
TESTING ................................................................................................................................................................................... 17 Environment ......................................................................................................................................................................... 17 Execution ............................................................................................................................................................................... 18
RESULTS ............................................................................................................................................................... 19 Comparison of average solving speed ....................................................................................................................... 19 Comparison in number of misses ................................................................................................................................. 21 Harder Sudokus .................................................................................................................................................................. 26
CONCLUSIONS ..................................................................................................................................................... 27 RECOMMENDATIONS AND POSSIBLE EXTENSIONS ............................................................................... 30 REFERENCES ....................................................................................................................................................... 31 APPENDIX A -‐ RESULTS ................................................................................................................................... 32 HUMAN -‐ SOLVING TIME & NUMBER OF MISSES ................................................................................................................ 32 HUMAN -‐ HARDER PUZZLES .................................................................................................................................................. 34 BRUTE-‐FORCE -‐ SOLVING TIME & NUMBER OF MISSES .................................................................................................... 34 BRUTE-‐FORCE -‐ HARDER PUZZLES ...................................................................................................................................... 37
APPENDIX B -‐ CODE .......................................................................................................................................... 38 SHARED .................................................................................................................................................................................... 38
6
Cell.java .................................................................................................................................................................................. 38 Solver.java ............................................................................................................................................................................. 39 SudokuSolver.java .............................................................................................................................................................. 42
SOLVERS ................................................................................................................................................................................... 44 HumanSolver.java .............................................................................................................................................................. 44 BruteforceSolver.java ....................................................................................................................................................... 55
TESTING ................................................................................................................................................................................... 57 run.sh ....................................................................................................................................................................................... 57 run_both_tests.sh ................................................................................................................................................................ 57 SudokuToExcel.java .......................................................................................................................................................... 57
APPENDIX C -‐ SUDOKU EXAMPLES .............................................................................................................. 62 NE.SE ........................................................................................................................................................................................ 62 HARDER PUZZLES ................................................................................................................................................................... 63
7
Terminology Algorithm:
An algorithm in this thesis refers to an ordinary computer science algorithm that can be used to solve a Sudoku.
Backtrack: An algorithm is backtracking if it can go back to a previous state, often with a recursive call.
Box: A sub grid that consists of 3×3 cells. There are 9 boxes in a normal Sudoku. Brute-force:
Also known as exhaustive search. Finds a solution by going through all possible solutions.
Candidate: Possible numbers that can be placed in a cell without breaking the Sudoku constraints.
Cell: A cell is the smallest building block of a Sudoku. A Sudoku normally consists of 9×9 cells. Each cell contains a value between 1 and 9.
Clue: A given number. In the beginning of a Sudoku game some of the numbers are already entered in the cells, these are called clues.
Grid: The Sudoku board. Normally consists of 9 rows × 9 columns of cells and 9 boxes. Pair: A relation between two cells, often related to their candidates. Used in techniques. Quad: A relation between four cells, often related to their candidates. Used in techniques. Region: Either refers to a row, column or box in the Sudoku. Rule: Refers to technique. Set:
Refers to either a pair or a triple. Technique:
A technique in this thesis refers to a technique used by a human to solve a Sudoku. A technique typically begins with matching a pattern, which leads to either being able to enter a new number in a cell or removing candidates from some cell(s).
Triple: A relation between three cells, often related to their candidates. Used in techniques. Unit: Refers to region.
8
Introduction
Preface This document is written for the course Degree Project in Computer Science, First Level, DD143X/dkand13 at the Royal Institute of Technology. We are both 3rd year students at the Computer Science programme at the Royal Institute of Technology, our supervisor for our work is Roberto Bresin and examiner is Mårten Björkman. In this thesis we investigate how solvers based on human techniques compare to known computer algorithms when solving Sudokus.
Sudoku Sudoku is a popular logic-based puzzle where the objective is to fill a 9×9 grid with numbers, with a subset of the solution already given, so that each column, each row, and each of the nine 3×3 sub-grids contains all of the numbers from 1 to 9.
Figure 1, a basic instance of a 9x9 Sudoku.
Figure 2, a solution of the instance given in fig. 1.
A Sudoku can vary in difficulty depending of various aspect including number of given numbers. Figure 1 gives an example of a basic Sudoku instance with 30 given numbers and the solution is presented in Figure 2 with filled in numbers displayed in red.
Sudoku Solvers We chose to make a human-like Sudoku solver because we wanted to see how the techniques a human use to solve a Sudoku compares to computer solvers. We find this interesting because we want to see if the solvers used on computer really are better than the methods used by a human or if the computer solves the puzzles faster simply because it can perform faster calculations then a human.
9
Solving techniques and algorithms The different algorithms that are used to solve Sudokus vary in complexity depending on approach. The brute-force algorithm fills in the missing numbers without any sense of logic and check afterwards if it was a valid placement or not. These algorithms use exhaustive search to solve Sudokus, which means a large amount of backtracking and guessing; Hence not suitable to be used by a human. An advantage with this method is that it will always find a solution. However this also means that it will try all different solutions and since there are 6.67 * 10^21 possible Sudokus it could take a very long time in the worst-case scenario. [1][2] A human rule based algorithm uses a series of techniques that are based upon the given restrictions of Sudoku. The techniques are then applied by the solver in order (from easiest to hardest) to find a possible number placement. One rule might be to check for any naked singles in a row, meaning searching for locations where one and only one number can be placed according to the Sudoku restrictions. A problem with this algorithm is that it does not guarantee that a solution will be found because the rules are not exhaustive, i.e. there are Sudokus that cannot be solved using only rules [3].
Problem statement A Sudoku can be solved using a brute-force algorithm [4] but it is more interesting to understand how a human would solve the same puzzle, and to implement his/her strategies. Therefore we want to find out how a human rule based solver compares to known computer based solvers and in which aspects a human is more efficient. Because there are a lot of different solving techniques and algorithms available we have initially limited this project to compare the following: A human rule based solver that we will create, a solver based on exhaustive search, and another solver based on exhaustive search that uses backtracking.
Purpose The purpose of our project is to create and implement a solver that can solve a Sudoku without guessing, just like a human. We will measure how good our solver is by implementing other well-known solvers and compare them in terms of speed, number of misses or wrong moves required to find a solution, and ability to solve harder Sudokus. We will therefore:
● Develop a solver that will match the behaviour of a human solving a Sudoku by only using non-guessing rules.
● Test that solver against as many different Sudokus of increasing difficulty as possible. ● Compare that solver against other solvers, where we hope that the human rule based
solver shall be the most efficient in one of the three aspects we compare: solving speed, number of misses or wrong moves required to find a solution, and ability to solve harder Sudokus.
10
Outline First of all we have included an abstract that summarizes this thesis in terms of what it is about and what our results and conclusions are. A table of content follows to give an overview of the document. Then we have included a statement of collaboration that will show the reader which student performed what work. After that we have a section named terminology with terms regularly used in the thesis. Then there is the introduction section. This is meant to introduce the reader to our problem and what the rest of the thesis will cover; ‘problem statement’ and ‘purpose’ are included here. After the introduction the thesis presents a more in depth background of Sudoku and Sudoku solvers. In the section that covers Sudoku solvers different algorithms and techniques are discussed. After that the approach is presented. The approach covers what kind of solvers we implemented, why we implemented them, how we did that, and how we tested them. The thesis takes up our result and conclusions towards the end of the thesis. The result section contains a lot of graphs and tables extracted from the results in the appendix while the conclusions contains discussion of those results. The last two sections are ‘Recommendations and possible extensions’ and ‘References’. The former chapter includes our recommendations of extensions and also include some recommendations if anyone would like to replicate something covered in this thesis. Some of the results and most of the code are included in appendices A and B respectively. Appendix C contains some examples of the Sudokus that we have used in our testing.
11
Background
Sudoku Sudoku is a logic based number puzzle represented by a square grid. The most common variant of Sudoku has a grid consisting of 9 * 9 cells that are divided into boxes that consist of 3 * 3 cells each. Numbers ranging from 1 to 9 shall be placed in the cells, filling the grid, so that every number occurs only once in each row, column and box. It exists different difficulty levels in Sudoku, where difficulty is based on how many clues are given from the beginning and partially on the layout of these. A Sudoku usually has a minimum of 17 clues and only one, distinct, solution. As previously mentioned there are 6.67 * 10^21 different Sudokus [1][2] but not all of them are playable by humans, at least not without guessing [3]. A common property of the unsolvable Sudokus is that they have multiple solutions, which means that not enough clues has been given to provide a distinct solution [5]. Sudoku became popular in Japan in the mid 80’s but similar games had existed before that. It became widely popular around 2004 when it started to appear in newspapers where it became an appreciated alternative to the traditional crosswords. Today Sudoku does not only appear in newspapers but also as computer games and in international Sudoku championships. [6]
Solving Sudokus A Sudoku game begins with some of the cells already filled in with clues and the aim is to fill the remaining empty cells. There is only one rule in Sudoku [7]:
“Each row, column and box must end up containing all of the numbers from 1 to 9.” This rule has an important side-effect, which is the basis of all solving techniques:
“Each number can only appear once in a row, column or box.”
Thus a Sudoku game ends when there is a number in every cell and the one rule of Sudoku is satisfied. There are many different ways to solve a Sudoku. Different algorithms and techniques work well in different occasions. In this section we will discuss some of the more common algorithms and techniques that can be used when solving Sudokus with a focus on the techniques that we are studying in this thesis [5].
Algorithms To solve a Sudoku with the help of a computer you would normally use some kind of algorithm or a combination of algorithms. In this section we will present some of the better-known algorithms with a focus on those we are going to investigate in this thesis.
12
Brute-force algorithm One of the better-known algorithms that are used when solving Sudokus on a computer is called brute-force. The brute-force algorithm is sometimes called an exhaustive search algorithm and it has become famous in computer science because of its striking characteristics: It always finds a solution because it will try all possible solutions hence it is not considered to be very effective [8]. A Sudoku solver that uses brute-force will visit all the empty cells and try to fill it with a number from the available choices. If no violations are found after an insertion it will continue to the next empty cell and start over. As soon as a violation to the Sudoku rules are found the algorithm will backtrack and increment the previous cell. [9]
Human algorithm
The algorithm that is used by most humans competing in Sudoku challenges is sometimes referred to as the pencil-and-paper algorithm [10] but we have chosen to call it the human algorithm. The human algorithm uses a number of techniques to solve a Sudoku without guessing [3]. A common trait of this algorithm is the use of candidates. Candidates are defined as all the possible numbers that could be placed in a cell given the Sudoku constraints. Candidates are often represented by humans as smaller numbers written in or beside the cell that they belong to (see figure 3).
Figure 3, a figure that shows a Sudoku box with all candidates filled in. [7]
A Sudoku solver that uses the human algorithm is often built with a loop that loops over techniques, beginning with easier and ending with harder techniques to emulate the general human behaviour [5].
Techniques The human algorithm uses techniques to solve a Sudoku, which can also be described as rules that each consists of a pattern followed by an action [3][10]. If a pattern is matched when performing a technique during a Sudoku session, the technique’s corresponding action will be executed before proceeding to the next technique. If an action is performed it will either place a number in a specific cell or remove one or more candidates from one or more cells. The concepts of the different techniques are often the same across the resources available but have no standardised terminology or general description of these techniques. Therefore, we decided on using the techniques and naming convention as described at sudokuwiki [5]. The following techniques are some of the most basic ones of solving a Sudoku and are the first six rules that the human solver uses at sudokuwiki [5].
13
Naked Single This technique is very simple and often the best way to begin a Sudoku game. Pattern: A cell only has a single candidate left. Action: Places that single candidate number in that cell.
Hidden Single Pattern: A region only has a single cell in which it can place a remaining number. Action: Places that number in that single cell.
Naked Pair/Triple Pattern: Two/three cells in the same region have a union of two/three candidates in common. Action: The candidates in this union are removed from the same unit.
Hidden Pair/Triple Pattern: Two/three cells in the same region have the last remaining two/three candidates for that region in common. Action: Any candidate(s) that are not a member of the pair/triple in the found cells are removed.
Naked Quad Pattern: Four cells in the same region have a union of four candidates in common. Action: The candidates in this union are removed from the same unit.
Box Line Reduction Pattern: The only occurrences of a candidate of cells in a row or column are in the same box. Action: Remove all other occurrences of this candidate in the rest of this box.
Pointing Pairs/Triples Pattern: The only occurrences of a candidate in a box are aligned in cells on a row or column. Action: Remove all other occurrences of this candidate in the rest of this row or column.
14
Approach After settling the problem statement, purpose and goals of this thesis we did some extended research about Sudoku solvers, and focused especially on those incorporating human based strategies. We found that most human based solvers use the same fundamental concept of applying rules in a specific order, and they did, more or less, only differ in terminology of these rules [10][5]. With these resources available we decided that we would base our human solver upon these established concepts. In terms of finding a solver utilizing a brute-force algorithm we found a range of different implementations in varying complexity. In the long run we settled on using a simple and fast solver written in java as a reference [11]. At first, this thesis was to cover two exhaustive search solvers in addition to the human rule based solver. The problem was that one of the brute-force algorithms used in one of the solvers did not terminate during initial testing due to unacceptable computing complexity. That is why we decided on only using two solvers and comparing these against each other. The puzzles we decided to use in our testing are the ones available at ne.se. Ne.se provides sixty puzzles in ranging difficulty from easy to hard [12]. We chose this set of puzzles because it is from a reliable source, they are of mixed complexity and typically the kind of Sudoku you would expect to find in a newspaper. In accordance to our goals we ought try to solve all of these puzzles with our human solver. Before we were done with the implementation of our solver we had the hypothesis that our human solver would match the brute-force solver in performance up to a certain point where the puzzles would become too difficult and the rules used in our solver would get too complicated to match the speed of trial and error.
Implementation The two solvers are both written in java and derives from a basic solver class (found in Appendix B, Shared) that handles initialization of the grid, reading Sudoku files, and verification of a solved Sudoku. The individual solvers then have additional methods for solving the Sudoku based on their different algorithm and/or techniques. We shared as many methods as possible between the two solvers to minimize the differences between the solvers to produce more reliable testing results. Both solvers begins with some administrative work like loading and parsing a Sudoku from file and also initialization of the grid, boxes and cells. Each cell is initialized with a number that is either a clue or a zero value if no clue was given in that cell. Each cell also gains a boxID at initialization, which lets the cell know which box it belongs to.
15
Human rule based solver The human solver uses a set of rules that the solver tries to match in a prioritized order. Each rule has a pattern and if successfully matched applies an action to the Sudoku. Before the main solving method is called, the candidates for each cell are initiated by checking the clues given. This is a precondition on which all of the followed techniques require. The main loop is located in the method called solve. Solve goes through all of the rules in an order of increasing complexity and decreasing probability of finding a match. If a rule pattern is found and the following action made a change to the Sudoku, the matched rule method returns true and the main loop starts over and tries to match the first rule again. A rule method will return false if a matched rule did not change the Sudoku or if the pattern was not matched at all. The main loop will repeatedly go through all of the rules until none of them returns true. When that happens the loop will break and the Sudoku will be verified by calling the method valid from the Solver class. The solve method will finally return true if the Sudoku is correctly solved and otherwise return false. The rule order can be seen in the following pseudo code example of the main loop. The description of these rules can be found in the background section of this thesis and the implementation of these in the code appendix (Appendix B, Solvers). boolean solve() := while(true) if nakedSingle() else if hiddenSingle() else if nakedSet() else if hiddenSet() else if nakedQuad() else if boxLineReduction() else if pointingSet() else break return valid() We settled on using these six rules because it proved to be enough for completing the set of Sudoku puzzles we earlier stated we would try to solve. There are more complex rules available at sudokuwiki [5].
Brute-force solver The brute-force solver is implemented with a smart exhaustive search that uses both ‘look ahead’ and backtracking. With ‘look ahead’ we mean that we implemented this algorithm to be smarter than the normal brute-force approach would with the help of candidates. Instead of trying 1-9 in every cell we first remove all numbers that exist in the same box, row and column (we find the cells candidates); Thereby lowering the amount of numbers that will be tried.
16
The actual solving is done by the method solve. Unlike solve implemented in the human solver that relies on a loop to run this method is implemented with backtracking in mind. This means that the solve method in the brute-force solver will call itself recursively to find a solution. The simple thoughts of brute-force and backtracking made the implementation of this solver easy and fast which is reflected in how many lines of code we have, which is approximately 70 in the brute-force solver compared to the approximately 660 lines in the human solver. The solver will first go through the grid, look for the first cell with a value of zero and extract the coordinates to the cell that contained the value zero. If, however, no zero was found the algorithm will return the method valid from the class Solver that validates the puzzle. If a zero was found it will look at all values of neighbouring cells in the same box, row and column and save these as possible candidates. It will then iterate over these candidates; calling solve again for each one of them. If a dead end is reached the current solve will return false to the one calling it which will make the caller try the next candidate. The solver is described in the following pseudo code example and the real implementation of the brute-force solver can be found in the code appendix (Appendix B, Solvers). boolean solve() := x = 0, y = 0 for x,y in grid: if grid[x][y].value == 0 found = true break if !(found) return valid() candidates = boolean[10] for i := 0 -> 9: candidates[grid[x][i].value] = true candidates[grid[i][y].value] = true for (cells in same box as cell x,y): candidates[cell.value] = true for j := 1 -> 9: if !(candidates[j]): grid[x][y].value = j if solve() return true grid[x][y].value = 0 return false
17
Testing
Environment To test our implementations we first modified the code and added some test-code: We added a counter of misses or wrong moves in both HumanSolver and the BruteforceSolver (found in Appendix B, Solvers). In the human solver the miss counter represents the number of rules that the solver has tried to match but didn’t succeed. In the brute-force solver it represents the numbers of wrong turns at a branch, the number of times the algorithm reached a dead end and had to backtrack. We also added a timer to see how long it took for the solver to complete. This code was added to SudokuSolver (found in Appendix B, Shared) since it was identical in both our solvers. To make this timer as fair as possible it only concerns the method calls to solve(). This means that we don’t include the time required to load the Sudoku from file into the grid, initialize the boxes, cells and candidates. We also wanted to be able to test our solvers from the command line, so we added support for sending parameters to SudokuSolver. There are a total of four parameters that can be sent:
1 filename: This will be interpreted as the path to the puzzle you want to solve. 2 solverID: This is specified as an identifier of which solver to use; “human” for
HumanSolver and “brute” for BruteforceSolver. 3 printer: This is used so that the user can tell the program how to print the results;
“human” for a human readable print and “computer” to be used later to get results. 4 test: This specifies which test the user wants to run; “run” for solving speed (runtime),
“miss” for counting of misses and “both” for both the tests above. To be able to run a test on all the available puzzles we wrote a small bash-script, called run.sh (found in Appendix B, Testing), which loops over all available puzzles. It takes some options defined below and sends them to SudokuSolver in the right order. Valid options are:
1 -s: Corresponds to solverID above. 2 -p: Corresponds to printer above. 3 -t: Corresponds to test above.
Example usage: sh run.sh -s human -p computer -t both The number of misses of a puzzle for a certain solver is always the same, so in that aspect it is enough to test each puzzle once with each solver. Unfortunately this is not the case when it comes to solving speed. So to get reliable results of the solving speed per puzzle we made one more bash script, called run_both_tests.sh (found in Appendix B, Testing), that calls run.sh multiple times and saves all output to a text file. It consists of two loops running a fixed number of loops; One that calls run.sh with the human solver and one with the brute-force solver. The loop that runs the human solver appends all output to a file called “human.txt” and the loop that runs the brute-force solver appends all output to “brute.txt”. Both of these are located in a test folder.
18
To handle all the information in the text files we wrote a new java class named SudokuToExcel (found in Appendix B, Testing). The purpose of this class was to take the two text files produced by run_both_tests.sh and get them into a nicer format for comparisons. It needs a sorted version of human.txt and brute.txt before beginning (!). When it starts it will create a new excel file called result.xls, read from one file at a time; Converting the file from a text file into an ArrayList where an element in the ArrayList corresponds to a row in the file. Then we loop over all these elements, only printing one row per unique Sudoku id, calculating average, minimum and maximum solving time as we go. The end result is a file, which we can open in other programs and get some nice charts and tables from.
Execution The test was performed on one of the desktop computers in the computer lab grön at Lindstedsvägen 3, 4th floor, KTH Royal Institute of Technology. The computer that we performed the tests on is running ubuntu 12.04 LTS and its system specifications are: Memory: 3.8 GiB Processor: Intel® Core™2 Quad CPU Q9550 @ 2.83GHz × 4 OS type: 64-bit We restarted the computer before carrying out the test to make sure no one was logged in and performing tasks in the background. This was done to make sure our tests were as accurate as possible. Then we ran the run_both_tests.sh with the loops running 100 times each. After that was done we executed SudokuToExcel and everything was done for the results. We ran it on all the 60 Sudokus that are available from ne.se [12]. To test the ability to solve harder Sudokus even further we also tested on the 10 first puzzles in Advanced Puzzle Pack 2 from angusj [13] and a weekly unsolvable (that we know is unsolvable for a human) from sudokuwiki [5]. Examples of the Sudokus used in the testing can be found in the examples appendix (Appendix C). The results from the tests can be seen in the next section and they are discussed in the ‘Conclusions’.
19
Results The test results are presented in three different subsections, each of these presents a view of the results reflecting the three aspects we want to compare. The results of the human solver are presented in blue colour and the brute-force solver in red. All of the Sudoku identifiers presented on the bottom axis are the same identifiers used at ne.se [12]. All results can be found in the results appendix (Appendix A).
Comparison of average solving speed The three charts in this section present the results of the average solving speed for the easy, medium and hard Sudoku puzzles respectively. Solving speed is presented in milliseconds on the left axis and Sudoku identifiers on the bottom axis. All the Sudokus are individually solved and measured 100 times to get a good average solving speed. All these results can be found as numbers in the results appendix (Appendix A). Sudokus of easy difficulty
Figure 4. Average solving speed of the sixteen easy Sudoku puzzles.
20
Sudokus of medium difficulty
Figure 5. Average solving speed of the twenty-nine medium Sudoku puzzles.
21
Sudokus of hard difficulty
Figure 6. Average solving speed of the fifteen hard Sudoku puzzles.
Comparison in number of misses The six charts presented in this section are the results of the number of misses the two solvers had for each of the Sudoku puzzles. The time aspect is omitted here because the amounts of misses are a constant factor because we do not use probabilistic algorithms in either of the solvers. The results are presented in two charts per level of difficulty because the differences between the solvers were too vast. All these results can be found as numbers in the results appendix (Appendix A). The left axis represents the number of misses for each of the Sudoku ID’s presented on the bottom axis.
22
Sudokus of easy difficulty
Figure 7. Number of misses of the sixteen easy Sudoku puzzles solved by the human solver.
Figure 8. Number of misses of the sixteen easy Sudoku puzzles solved by the brute-force solver.
23
Sudokus of medium difficulty
Figure 9. Number of misses of the twenty-nine medium Sudoku puzzles solved by the human solver.
24
Figure 10. Number of misses of the twenty-nine medium Sudoku puzzles solved by the brute-force solver.
25
Sudokus of hard difficulty
Figure 11. Number of misses of the fifteen hard Sudoku puzzles solved by the human solver.
Figure 12. Number of misses of the fifteen hard Sudoku puzzles solved by the brute-force solver.
26
Harder Sudokus This section presents the results when attempting to solve harder Sudokus. The Sudokus used in this test are available from the Advanced Puzzle Pack 2 on A. Johnson’s website [13]. The results displayed below in Figure 13 are the results gained from running ‘sh run.sh -s brute -p human -t both’ and ‘sh run.sh -s human -p human -t both’. (These are also available in Appendix A).
BruteforceSolver: Solved puzzles_harder/puzzle001.ss in: 27ms with 5124 misses. Solved puzzles_harder/puzzle002.ss in: 23ms with 2941 misses. Solved puzzles_harder/puzzle003.ss in: 28ms with 4185 misses. Solved puzzles_harder/puzzle004.ss in: 16ms with 2215 misses. Solved puzzles_harder/puzzle005.ss in: 25ms with 3169 misses. Solved puzzles_harder/puzzle006.ss in: 23ms with 4250 misses. Solved puzzles_harder/puzzle007.ss in: 27ms with 3668 misses. Solved puzzles_harder/puzzle008.ss in: 21ms with 2418 misses. Solved puzzles_harder/puzzle009.ss in: 25ms with 7317 misses. Solved puzzles_harder/puzzle010.ss in: 27ms with 3763 misses. Solved puzzles_harder/unsolvable.ss in: 25ms with 23598 misses. -‐-‐-‐-‐-‐-‐-‐-‐-‐-‐ HumanSolver: Failed to solve puzzles_harder/puzzle001.ss. Failed to solve puzzles_harder/puzzle002.ss. Failed to solve puzzles_harder/puzzle003.ss. Failed to solve puzzles_harder/puzzle004.ss. Failed to solve puzzles_harder/puzzle005.ss. Failed to solve puzzles_harder/puzzle006.ss. Failed to solve puzzles_harder/puzzle007.ss. Failed to solve puzzles_harder/puzzle008.ss. Failed to solve puzzles_harder/puzzle009.ss. Failed to solve puzzles_harder/puzzle010.ss. Failed to solve puzzles_harder/unsolvable.ss.
Figure 13. This is the output from an attempt of solving a harder set of Sudokus with both of the solvers respectively. The lines above the delimiter are the results from the brute-force solver and the ones below the output from the human solver.
27
Conclusions Our hypothesis, which we presented in the approach, has proven to be quite accurate. The human solver certainly matched the brute-force solver in performance up to a certain point where the puzzles became too difficult. This was not because our rules got too complicated to match the brute-force solver but rather that our rules were not enough to solve the harder Sudokus. In our opinion this thesis turned out above expectations. We succeeded in covering everything in the purpose, and we found a lot of great answers to our concerns and problem statement. We will begin this section by addressing the questions in the purpose, then continue to interpret the results from the section above and lastly we will give an answer to the problem statement:
● Develop a solver that will match the behaviour of a human solving a Sudoku by only using non-guessing rules.
We have developed a solver that we think matches a human solving a Sudoku acceptably. It fulfils the requirement of only using non-guessing rules since we only implemented rules that use pattern matching; Hence ‘non guessing’. The behaviour of a human is not that easy to capture because a human behaviour can sometimes seem quite random; Sometimes they begin in the top-left corner and sometimes the lower-right, it is impossible to say for sure. Therefore we believe that our loop with all rules we implemented in an order of increasing complexity is as close as we can get. When we developed the human solver we looked at the rules at sudokuwiki [5] as described in approach. We only implemented six of the 32 rules defined there because we saw that those six, basic, rules were enough to solve all the Sudokus that we wanted to test on. As a result of this our human solver matches the behaviour of a general human trying to solve a typical Sudoku found in a newspaper and not the behaviour of a Sudoku professional that competes in solving Sudoku. To match that person we would have needed to implement more, if not all, rules found on sudokuwiki [5].
● Test that solver against as many different Sudokus of increasing difficulty as possible. We have tested our solver against 71 different Sudokus of increasing difficulty. We would have wanted to test on even more puzzles but this goal became a bit blunted by the fact that we did not find the extensive database of Sudokus that we were hoping for. However we still think that we fulfilled this goal by choosing archetype Sudokus from a reliable source [6] and testing against very hard puzzles [13]. We also tried to compensate for the somewhat low amount of Sudokus by testing each puzzle several (100) times to get accurate data. We did not include more puzzles in the test because of several things. Firstly, converting those 60 puzzles from ne.se to the .ss format took at least 2 hours. We continued to search but we did not find any more Sudokus as reliable as those first 60. Lastly we did not think that the time
28
spent looking for and/or converting puzzles was worthwhile when what we really wanted to do was to implement the solvers.
● Compare that solver against other solvers, where we hope that the human rule based solver shall be the most efficient in one of the three aspects we compare: solving speed, number of misses or wrong moves required to find a solution, and ability to solve harder Sudokus.
Overall our human solver performed above expectations and it seems very efficient in both solving speed and number of misses. The last aspect is harder to analyse since we don’t really know how many more rules we must implement to be able to solve the last 11 Sudokus and how that would affect our solving time and number of misses. Regarding the ‘other solvers’ we only implemented one of the two we first thought of. We felt we could do this because we ended up implementing the smarter of the two anyway, which is already stated in our approach. A comparison between our human solver and the brute-force solver on the different aspects mentioned above is included below.
1 Solving speed The human solver is faster in the majority of the average solving speed tests than the brute-force solver with only a few exceptions. By looking at the result of the speed tests we can see that only three Sudokus (with id 0830(easy), 0939(easy), and 0845(medium)) of the total 60 puzzles were solved faster with the brute-force solver (see Figure 4, Figure 5 and Figure 6). Why these particular Sudokus were solved faster with brute-force can be motivated by comparing the result of number of misses for those Sudoku IDs. The amount of backtracking done is minimal in relation to any of the other 57 Sudokus. In fact, none of the three Sudokus exceed 1000 misses (see Figure 8, Figure 10 and Figure 12). We can also see that the number of misses for these three Sudokus when running the human solver is somewhat close to the average amount of misses. This might be the reason why the brute-force solver managed to beat the human solver in terms of speed, if only in these three cases, because the puzzles were suitable to solve with a backtracking solving approach. A reason why the human solver is faster could be that the solving techniques are very time efficient given the probability to find a matching pattern and eliminating possible candidates. However, it is important that we take into consideration that the rules used are quite simple. More complex rules might not showcase this level of time efficiency and in the long run might not be able to beat the stability in solving speed of the brute-force solver.
2 Number of misses or wrong moves required to find a solution The results from this test might be interpreted as extremely successful from the human solvers point of view if not properly examined. As the two solvers uses different approaches it is difficult to equally compare wrong moves and numbers missed by presenting them side by side.
29
The miss count is measured as unmatched rules in human solver and as number of backtracking steps in the brute-force solver. To get a more comparable view of the data we can multiply the number of misses by 9^3 and interpret the resulting amount as the unmatched patterns for each of the rows, columns and boxes individually per technique. This is a good approximation as most of the rules goes through all of the three region types (row, column and box), containing nine units each, to find a pattern match. If we do this modification to the miss count and compare it of both solvers, we get about 12000 misses for the human solver and about 94000 misses for the brute-force solver. Even though the difference is not as extreme as before the human solver still has a significantly lower amount of misses. (These numbers are calculated from the data in Appendix A). Why the human solver performs better in this test might be because the techniques used depends on matching patterns before applying actions while the brute-force solver actively uses backtracking as tool to search for an solution.
3 Ability to solve harder Sudokus The human solver managed to solve all of the 60 Sudokus from ne.se [12] in difficulties ranging from easy to hard in accordance to the grading performed by ne.se. The testing results of the very hard Sudokus from our additional Sudoku resource [13] can be seen in the last section of results (see Figure 13). While the brute-force solver had a solving speed comparable to previous ones the human solver failed to find a solution to these 11 puzzles. The reason why the human solver failed is because the six rules we have implemented are not enough to solve these particular Sudokus. If we import the Sudokus to the human like solver available at sudokuwiki [5] we can see that techniques like “simple colouring” is required to find a solution. While the online solver manages to complete most of the harder Sudokus, the 11th one in this harder set is unsolvable even for this solver using all of its 32 techniques. A conclusion of this test is that there are Sudoku puzzles that are unsolvable using the definition of human techniques from sudokuwiki [5]. Final conclusions To get a good answer to our problem statement we summarize the conclusions above. We have found that the human solver is comparable to known computer based solvers in terms of solving speed and number of misses. When solving harder Sudokus we cannot be sure if our human solver can produce a solution. However, when solving typical Sudokus found in newspapers we have found that our solver is comparable to the brute-force solver. In our case we have also found that the human solver is generally more efficient than the brute-force solver in the aspects of solving speed and number of misses.
30
Recommendations and possible extensions We hope that this thesis can be a useful resource in future work. Some extensions that we have thought about are:
● Compare the solvers above even further and/or include more kinds of solvers in the comparison.
● Develop a solver that uses several algorithms in different occasions to solve puzzles of different characteristics.
● Build a better and more complex human solver with more rules that will solve more puzzles.
● Compare our human solver above to an already existing human solver in an attempt to make a better human solver.
If someone would like to replicate something in this thesis we would like to give some recommendations so that they would know what to expect:
● Find a reliable database of Sudokus that you can access or a good Sudoku generator before beginning with the testing. You can either do this by searching the web or by building your own. We found some previous work on Sudokus done by students of previous years that described they found a free database containing 10000 puzzles that they could use for testing. We made the assumption that we could simply use the same database for our testing but when we tried to find it we discovered that it had been removed. This alone ended up costing us several hours of work of looking for reliable puzzles and when we finally found them on ne.se [6] they were in PDF format so we had to convert all 60 of them manually before we could continue with the testing.
● Don’t underestimate the time required to implement the solvers, especially the human techniques. By the time we were supposed to be done with the implementation of the solvers we had written them hastily and the code was neither nice looking, effective or easy to understand. It took us at least twice the time it took to write the first version of code to clean, optimize and document it with comments.
Have fun! This is probably the most fun study either of us has done and we hope that you will enjoy it as well. Our only regret is that we didn’t really have the time to implement more rules to the human solver.
31
References [1] Felgenhauer B, Jarvis F. Enumerating possible Sudoku grids. [Internet]. 2005 [cited 2013 Mar 21]. Available from: http://www.afjarvis.staff.shef.ac.uk/sudoku/ [2] Felgenhauer B, Jarvis F. Mathematics of Sudoku I. [Internet]. 2006 [cited 2013 Mar 21]. Available from: http://www.afjarvis.staff.shef.ac.uk/sudoku/felgenhauer_jarvis_spec1.pdf [3] Mepham M. Solving Sudoku. [Internet]. 2005 [cited 2013 Apr 9]. Available from: http://www.sudoku.org.uk/PDF/Solving_Sudoku.pdf [4] Björkman M. Project Ideas. [Internet]. 2013 [cited 2013 Feb 4]. Availible from: http://www.csc.kth.se/utbildning/kth/kurser/DD143X/dkand13/ProjectIdeas/ [5] Stuart A. Sudoku Solver. [Internet]. 2005 [updated 2012 Oct 22; cited 2013 Mar 4]. Available from: http://www.sudokuwiki.org/sudoku.htm [6] Nationalencyklopedin. sudoku. [Internet]. c2013 [cited 2013 Apr 8]. Availible from: http://www.ne.se/lang/sudoku [7] Johnson A. Solving Sudoku. [Internet] c2005 [cited 2013 Apr 10 ]. Available from: http://angusj.com/sudoku/hints.php [8] Kann V. Föreläsning 7, Metod 2: Totalsökning. [Internet]. c2012 [cited 2013 Apr 10]. Available from: http://www.csc.kth.se/utbildning/kth/kurser/DD1352/adk12/schema/ADK12-F7.pdf [9] Moler C. Cleve’s Corner: Solving Sudoku with MATHLAB. [Internet]. 2009 [cited 2013 Apr 9]. Available from: http://www.mathworks.se/company/newsletters/news_notes/2009/clevescorner.html [10] Crook J. F. A Pencil-and-Paper Algorithm for Solving Sudoku Puzzles. Notices of the AMS. [Internet]. 2009 [cited 2013 Feb 1]; 56(4):460-468. Available from: http://www.ams.org/notices/200904/200904-full-issue.pdf [11] st0le. Sudoku Solver(Bruteforce). [Internet]. 2010 [cited 2013 Mar]. Available from: http://code.activestate.com/recipes/577314-sudoku-solver-bruteforce/?in=user-4174421 [12] Nationalencyklopedin. Sudoku. [Internet]. c2013 [cited 2013 Apr 8]. Availible from: http://www.ne.se/static/entertainment/sudoku.jsp [13] Johnson A. Simple Sudoku. [Internet]. c2002 [cited 2013 Feb 25]. Available from: http://angusj.com/sudoku/
32
Appendix A - Results
Human - solving time & number of misses
ID Complexity Average solving speed (ms)
Minimum solving speed (ms)
Maximum solving speed (ms)
Number of misses
0818 easy 6.25 6 7 9
0830 easy 7.94000005722046 7 51 19
0842 easy 6.78999996185303 6 16 14
0903 easy 6.15000009536743 5 35 10
0915 easy 7.92000007629394 7 9 17
0927 easy 5.80000019073486 5 24 9
0939 easy 7.23000001907349 6 53 18
0948 easy 49.1100006103516 9 66 42
0951 easy 8.15999984741211 7 72 14
1012 easy 6.94999980926514 6 50 13
1024 easy 4.30999994277954 4 9 1
1036 easy 7.38000011444092 7 8 16
1048 easy 6.98999977111816 6 7 17
1109 easy 4.8600001335144 4 49 3
1121 easy 7.69000005722046 7 10 18
1136 easy 5.44999980926514 5 9 12
0815 medium 6.84999990463257 6 9 10
0821 medium 17.1100006103516 7 18 24
0827 medium 49.0299987792969 13 51 16
0833 medium 10.5799999237061 8 11 15
0839 medium 15.1400003433228 11 16 17
0845 medium 12.1099996566772 7 13 16
33
0851 medium 34.2200012207031 14 35 23
0906 medium 10.8599996566772 6 11 18
0912 medium 9.97999954223633 8 11 21
0918 medium 7.96999979019165 7 8 8
0924 medium 22.75 12 23 22
0930 medium 8.63000011444092 7 9 18
0936 medium 50.7099990844727 18 52 22
0942 medium 25.8500003814697 7 27 32
1001 medium 6.30000019073486 6 8 10
1009 medium 47.810001373291 15 50 32
1015 medium 7.69999980926514 7 9 13
1021 medium 8.35000038146973 8 9 17
1027 medium 13.2299995422363 5 14 22
1033 medium 7.5 7 8 16
1039 medium 10.3999996185303 8 11 17
1045 medium 6.80000019073486 6 19 7
1051 medium 15.1199998855591 8 16 12
1106 medium 48.060001373291 9 49 26
1112 medium 62.0200004577637 5 67 29
1118 medium 8.84000015258789 8 12 20
1124 medium 8.19999980926514 8 9 17
1130 medium 8.5 8 9 11
1140 medium 7.82000017166138 6 8 13
0812 hard 9.11881160736084 9 10 15
0824 hard 12.5100002288818 12 18 13
0836 hard 10.1300001144409 10 11 18
0848 hard 13.2600002288818 13 14 9
34
0909 hard 7.05000019073486 6 11 8
0921 hard 10.7799997329712 8 12 22
0933 hard 17.3799991607666 9 18 17
0945 hard 8.82999992370606 8 27 17
1006 hard 11.9799995422363 7 13 12
1018 hard 8.64999961853027 8 11 15
1030 hard 6.07999992370606 5 14 8
1042 hard 18.8099994659424 11 19 17
1103 hard 10.0900001525879 9 16 14
1115 hard 8.11999988555908 7 69 17
1127 hard 9.60999965667725 9 10 16
Human - harder puzzles Failed to solve puzzles_harder/puzzle001.ss. Failed to solve puzzles_harder/puzzle002.ss. Failed to solve puzzles_harder/puzzle003.ss. Failed to solve puzzles_harder/puzzle004.ss. Failed to solve puzzles_harder/puzzle005.ss. Failed to solve puzzles_harder/puzzle006.ss. Failed to solve puzzles_harder/puzzle007.ss. Failed to solve puzzles_harder/puzzle008.ss. Failed to solve puzzles_harder/puzzle009.ss. Failed to solve puzzles_harder/puzzle010.ss. Failed to solve puzzles_harder/unsolvable.ss.
Brute-force - solving time & number of misses
ID Complexity Average solving speed (ms)
Minimum solving speed (ms)
Maximum solving speed (ms)
Number of misses
0818 easy 23.8899993896484 23 69 4459
0830 easy 6.84999990463257 6 30 941
0842 easy 264.980010986328 76 271 919801
0903 easy 17.3299999237061 16 76 3187
0915 easy 114.839996337891 64 118 256977
35
0927 easy 48.6399993896484 47 66 10372
0939 easy 2 1 26 180
0948 easy 86.0100021362305 52 90 133623
0951 easy 69.5800018310547 65 90 56787
1012 easy 78.0599975585938 74 84 99532
1024 easy 26.3799991607666 13 27 6183
1036 easy 74.4000015258789 65 77 87382
1048 easy 58.439998626709 55 73 12624
1109 easy 63.0099983215332 57 232 17858
1121 easy 164.229995727539 64 176 472687
1136 easy 101.730003356934 25 111 203181
0815 medium 64.6399993896484 62 68 39988
0821 medium 55.6699981689453 24 58 11707
0827 medium 29.7099990844727 28 113 5600
0833 medium 59.560001373291 7 62 14295
0839 medium 72.8099975585938 52 75 72972
0845 medium 10.1099996566772 7 272 969
0851 medium 65.8000030517578 62 68 41840
0906 medium 61.2999992370606 18 63 22786
0912 medium 61.4700012207031 58 69 19584
0918 medium 62.4000015258789 58 121 29138
0924 medium 62.1100006103516 57 75 28757
0930 medium 60.7799987792969 56 63 21218
0936 medium 16.4599990844727 15 86 3195
0942 medium 57.9099998474121 2 62 12900
1001 medium 62.3400001525879 58 71 29881
1009 medium 72.8300018310547 69 85 71609
36
1015 medium 60.6500015258789 57 83 21093
1021 medium 12.039999961853 9 13 2080
1027 medium 90.4599990844726 28 116 144924
1033 medium 58.6100006103516 54 81 16892
1039 medium 63.4300003051758 59 89 32461
1045 medium 70.6999969482422 64 73 63678
1051 medium 58.8400001525879 56 62 16131
1106 medium 216.210006713867 19 231 702962
1112 medium 62.4199981689453 59 73 26854
1118 medium 63.3300018310547 57 281 21307
1124 medium 72.7600021362305 69 183 72035
1130 medium 18.7399997711182 18 37 3476
1140 medium 87.0100021362305 83 114 144910
0812 hard 65.1980209350586 60 68 43550
0824 hard 110.360000610352 60 113 235230
0836 hard 47.9599990844727 47 63 10009
0848 hard 59.1300010681152 8 62 19380
0909 hard 65.0199966430664 62 68 40058
0921 hard 61.9199981689453 59 69 27012
0933 hard 79.3899993896484 63 83 100176
0945 hard 68.4800033569336 65 73 58464
1006 hard 66.2099990844726 64 71 43762
1018 hard 9.5600004196167 8 65 1404
1030 hard 67.0899963378906 64 94 50496
1042 hard 59.8800010681152 57 67 18803
1103 hard 17.5200004577637 16 63 3082
1115 hard 274.529998779297 67 281 1018505
37
1127 hard 36.4099998474121 35 81 7186
Brute-force - harder puzzles
Solved puzzles_harder/puzzle001.ss in: 27ms with 5124 misses. Solved puzzles_harder/puzzle002.ss in: 23ms with 2941 misses. Solved puzzles_harder/puzzle003.ss in: 28ms with 4185 misses. Solved puzzles_harder/puzzle004.ss in: 16ms with 2215 misses. Solved puzzles_harder/puzzle005.ss in: 25ms with 3169 misses. Solved puzzles_harder/puzzle006.ss in: 23ms with 4250 misses. Solved puzzles_harder/puzzle007.ss in: 27ms with 3668 misses. Solved puzzles_harder/puzzle008.ss in: 21ms with 2418 misses. Solved puzzles_harder/puzzle009.ss in: 25ms with 7317 misses. Solved puzzles_harder/puzzle010.ss in: 27ms with 3763 misses. Solved puzzles_harder/unsolvable.ss in: 25ms with 23598 misses.
38
Appendix B - Code
Shared
Cell.java import java.util.HashSet; /** * This class represents an abstract sudoku cell. * For each Cell it holds the Cell's value, the * Cell's candidates and the boxID that this * particular Cell belongs to. * * @version 1.0 * @author Andreas Broström * @author Simon Johansson */ public class Cell { private int value; public int boxID; public HashSet<Integer> candidates; /** * This is called upon initialization and will * set the Cell's value and initial candidates. * * @param value is the value that will be held by * the Cell. */ public Cell(int value){ this.value = value; this.candidates = new HashSet<Integer>(9); if(value == 0){ for(int i=1;i<=9;i++){ this.candidates.add(i); } } } /** * Sets the Cell's value. * * @param inputValue will overwrite the current * value held by the Cell. */ public void setValue(int inputValue){ this.value = inputValue; this.candidates.clear(); } /** * Gets the Cell's value. * * @return the value of this Cell. */
39
public int getValue(){ return this.value; } }
Solver.java import java.io.BufferedReader; import java.io.DataInputStream; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; /** * This class tries to solve a sudoku using human techniques * with the help of Solver. * * @version 1.0 * @author Andreas Broström * @author Simon Johansson * */ public class Solver { public Cell[][] grid; public HashMap<Integer, ArrayList<Integer[]>> boxMap; /** * Loads a sudoku from file into a String. * * @param filename is the location and name of the simple * sudoku puzzle. ex: 'puzzles/medium[1027].ss'. * * @return a string representation of the sudoku-‐file. */ public String load_sudoku(String filename){ StringBuilder builder = new StringBuilder(); try{ FileInputStream fstream = new FileInputStream(filename); DataInputStream in = new DataInputStream(fstream); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String strLine; while ((strLine = br.readLine()) != null) { strLine = strLine.replace("|","").replace("-‐", "").replace("\n", "").replace(".", "0"); builder.append(strLine); } in.close(); }catch(Exception e){ System.err.println("Error: " + e.getMessage()); } return builder.toString(); } /** * Initlializes the sudoku grid from a string of numbers. It * goes through all Cell's, initializing and sets a value * according to strSudoku. (to the file)
40
* * @param filename is a simple sudoku file (.ss) that is * sent to load_sudoku for loading. */ protected void initGrid(String filename){ grid = new Cell[9][9]; String strSudoku = load_sudoku(filename); for(int row=0;row<9;row++){ for(int col=0;col<9;col++){ grid[row][col] = new Cell(Integer.parseInt(strSudoku.charAt(9*row + col)+"")); } } } /** * Initializes the boxID's of all Cell's in the grid. boxID is * an int from 1 to 9 which does so that the program quickly * can determine in which box a cell belongs to. */ protected void initBoxID(){ for(int row = 0; row < 3; row++){ for(int col = 0; col < 3; col++){ for(int dRow = 0; dRow < 3 ;dRow++){ for(int dCol = 0; dCol < 3; dCol++){ grid[row+dRow*3][col+dCol*3].boxID = 1 + (dCol + dRow * 3); } } } } } /** * Initializes and fills the boxMap with the coordinates * of all Cell's that belong to a box for each box. */ protected void initBoxMap(){ boxMap = new HashMap<Integer, ArrayList<Integer[]>>(); for(int boxNum = 1;boxNum<=9;boxNum++){ boxMap.put(new Integer(boxNum), new ArrayList<Integer[]>(9)); } for(int rowChunk=0;rowChunk<3;rowChunk++){ for(int colChunk=0;colChunk<3;colChunk++){ for(int rowBox=0;rowBox<3;rowBox++){ for(int colBox=0;colBox<3;colBox++){ Integer[] coords = new Integer[]{rowBox+rowChunk*3,colBox+colChunk*3}; boxMap.get(1+(rowChunk*3 + colChunk)).add(coords); } } } } } /** * Finds all Cell's in a specific row. * * @param rowNr points to a specific row in the grid. * * @return an ArrayList with all coordinates to the * Cell's in that particular row.
41
*/ public ArrayList<Integer[]> getRow(int rowNr){ ArrayList<Integer[]> row = new ArrayList<Integer[]>(); for(int col=0;col<9;col++){ row.add(new Integer[]{rowNr,col}); } return row; } /** * Finds all Cell's in a specific column. * * @param colNr points to a specific column in the grid. * * @return an ArrayList with all coordinates to the * Cell's in that particular column. */ public ArrayList<Integer[]> getCol(int colNr){ ArrayList<Integer[]> col = new ArrayList<Integer[]>(); for(int row=0;row<9;row++){ col.add(new Integer[]{row,colNr}); } return col; } /** * Finds all Cell's in a specific box. * * @param boxID points to a specific box in the grid. * * @return an ArrayList with all coordinates to the * Cell's in that particular box. */ public ArrayList<Integer[]> getBox(int boxID){ return boxMap.get(boxID); } /** * Checks if the grid fullfills the sudoku contraints. * * @return boolean which is true if the puzzle fullfills * the sudoku contraints and false otherwise. */ public boolean valid(){ int cellVal; ArrayList<Integer[]> unit; ArrayList<Integer> unitValues; for (int unitSelect = 0; unitSelect < 3; unitSelect++) { for (int pos = 0; pos < 9; pos++) { unitValues = new ArrayList<Integer>(9); unit = new ArrayList<Integer[]>(9); if (unitSelect == 0) { unit = getRow(pos); } else if (unitSelect == 1) { unit = getCol(pos); } else if (unitSelect == 2) {
42
unit = getBox(pos+1); } for (Integer[] coordinates : unit) { cellVal = grid[coordinates[0]][coordinates[1]].getValue(); if (cellVal == 0) { return false; } else if (!unitValues.contains(cellVal)){ unitValues.add(cellVal); } else { return false; } } } } return true; } }
SudokuSolver.java /** * This class is used as an interface so that run.sh * can solve several sudokus from the command line * with the option of choosing solver. * * @version 1.0 * @author Andreas Broström * @author Simon Johansson */ public class SudokuSolver { static String filename; static String printer = "human"; static String test = "both"; static boolean solved = false; /** * Provides support to be run both with or * without arguments. * * @param args is either empty or looks like this: * {filename, solverID, printer, test} * * @param filename is the path to the sudoku you * want to solve. * * @param printer is an option that decides how the result * will be presented. "human" form human readable and * "computer" for computer readable. * * @param test defines what test is to be presented. "run" is * the runtime of the solver, "miss" is the missCount and * everything else gets interpreted as both of them. * * @param solverID is an abbreviation of which * solver you would like to use. * * @example An example use could look like this: * "java SudokuSolver puzzles/hard[1115].ss human human run"
43
*/ public static void main(String[] args) { int missCount = 0; long t0 = 0; long t1 = 0; /* Handling input to the program: * what solver to use, print format and tests. */ if (args.length > 1) { filename = args[0]; if (args.length > 2) { printer = args[2]; if (args.length > 3) { test = args[3]; } } if (args[1].equalsIgnoreCase("brute")) { BruteforceSolver bfs = new BruteforceSolver(filename); t0 = System.currentTimeMillis(); solved = bfs.solve(); t1 = System.currentTimeMillis(); missCount = bfs.missCount; } else if (args[1].equalsIgnoreCase("human")) { HumanSolver hs = new HumanSolver(filename); t0 = System.currentTimeMillis(); solved = hs.solve(); t1 = System.currentTimeMillis(); missCount = hs.missCount; } else { System.out.println("Not a valid solver!"); } } else { filename = "puzzles/hard[1115].ss"; HumanSolver hs = new HumanSolver(filename); t0 = System.currentTimeMillis(); solved = hs.solve(); t1 = System.currentTimeMillis(); missCount = hs.missCount; } //Handles output if (solved) { if (test.equalsIgnoreCase("run")) { if (printer.equalsIgnoreCase("human")) { System.out.println("Solved " + filename + " in: " + (t1 -‐ t0) + "ms."); } else if (printer.equalsIgnoreCase("computer")) { String[] fileinfo = filename.split("/|.ss")[1].split("\\["); System.out.println(fileinfo[1].split("\\]")[0] + "$" + fileinfo[0] + "$" + (t1 -‐ t0)); } } else if (test.equalsIgnoreCase("miss")) { if (printer.equalsIgnoreCase("human")) { System.out.println("Solved " + filename + " in: " + missCount + " misses."); } else if (printer.equalsIgnoreCase("computer")) { String[] fileinfo = filename.split("/|.ss")[1].split("\\["); System.out.println(fileinfo[1].split("\\]")[0] + "$" + fileinfo[0] + "$" + missCount); } } else {
44
if (printer.equalsIgnoreCase("human")) { System.out.println("Solved " + filename + " in: " + (t1 -‐ t0) + "ms with " + missCount + " misses."); } else if (printer.equalsIgnoreCase("computer")) { String[] fileinfo = filename.split("/|.ss")[1].split("\\["); System.out.println(fileinfo[1].split("\\]")[0] + "$" + fileinfo[0] + "$" + (t1 -‐ t0) + "$" + missCount); } } } else { System.out.println("Failed to solve " + filename + "."); } } }
Solvers
HumanSolver.java import java.util.ArrayList; import java.util.HashSet; import java.util.Set; /** * This class tries to solve a sudoku using human techniques * with the help of Solver. * * @version 1.0 * @author Andreas Broström * @author Simon Johansson * * @see Solver */ public class HumanSolver extends Solver{ public int missCount = 0; private ArrayList<Integer[]> unit; /** * Initializes the grid, the boxID's and the boxMap and * updates all candidates for the first time. * * @param filename is the name of a sudoku file in the simple * sudoku format (.ss). */ public HumanSolver(String filename) { initGrid(filename); initBoxID(); initBoxMap(); updateCandidates(); } /** * Loops trough the rules until a solution is found or until * no successful rules can be applied. * * @return boolean which is true if the sudoku was solved and * false otherwise.
45
*/ public boolean solve() { while(true) { //Set refers to either pair or triple if (ruleNakedSingles()) { //Naked single found } else if (ruleHiddenSingle()) { //Hidden single found missCount += 1; } else if (ruleNakedSet()) { //Naked set found. missCount += 2; } else if (ruleHiddenSet()) { //Hidden set found! missCount += 3; } else if (ruleNakedQuad()) { //Naked quad found missCount += 4; } else if (ruleBoxLineReduction()) { //Box line reduction found missCount += 5; } else if (rulePointingSet()) { //Pointing set found missCount += 6; } else { // No rule could be matched break; } } return valid(); } /** * Go through all cells and updates their candidates */ public void updateCandidates() { for (int row = 0; row < 9; row++) { for (int col = 0; col < 9; col++) { updateCellClosure(row,col); } } } /** * Removes the cell value from the candidate lists * of the other cells in the same units * * @param cellRow a coordinate specifying in which row the * targeted cell can be found. * * @param cellCol a coordinate specifying the column of the * targeted cell can be found. */ public void updateCellClosure(int cellRow, int cellCol) { if (grid[cellRow][cellCol].getValue() != 0) { int cellValue = grid[cellRow][cellCol].getValue();
46
for (int unitSelect = 0; unitSelect < 3; unitSelect++) { if (unitSelect == 0) { unit = getRow(cellRow); } else if (unitSelect == 1) { unit = getCol(cellCol); } else if (unitSelect == 2) { unit = getBox(grid[cellRow][cellCol].boxID); } for (Integer[] elem : unit) { int row = elem[0]; int col = elem[1]; if (cellRow != row || cellCol != col) { if (grid[row][col].candidates.contains(cellValue)) { grid[row][col].candidates.remove((Object)cellValue); } } } } } } /** * A rule that looks for any single candidates, if * so set the value to the single candidate * * @return boolean which is true if the rule succeeded * and the grid is changed or false otherwise */ private boolean ruleNakedSingles() { for (int row = 0; row < 9; row++) { for (int col = 0; col < 9; col++) { HashSet<Integer> cellCandidates = grid[row][col].candidates; if (cellCandidates.size()==1) { grid[row][col].setValue(cellCandidates.iterator().next()); updateCellClosure(row, col); return true; } } } return false; } /** * This rule looks for hidden singles, defined * here: http://www.sudokuwiki.org/Getting_Started * * @return boolean which is true if the rule succeeded * and the grid is changed or false otherwise */ private boolean ruleHiddenSingle() { int [] unitCandidates = new int[10]; // For each row, column or boxID for (int unitSelect = 0; unitSelect < 3; unitSelect++) { for (int i = 0; i < 9; i++) {
47
if (unitSelect == 0) { unit = getRow(i); } else if (unitSelect == 1) { unit = getCol(i); } else if (unitSelect == 2) { unit = getBox(i + 1); } for (int n = 1; n <= 9; n++) { unitCandidates[n] = 0; } for (Integer[] elem:unit) { int row = elem[0]; int col = elem[1]; if (grid[row][col].getValue() == 0) { for (int cand:grid[row][col].candidates) { unitCandidates[cand] += 1; } } } int foundDigit = 0; for (int n = 1; n <= 9; n++) { // Check for hidden single if (unitCandidates[n] == 1) { // Found hidden single foundDigit = n; break; } } // If a hidden single was found, check what cell // contained that hidden single and set cell value if (foundDigit != 0) { for (Integer[] elem:unit) { int row = elem[0]; int col = elem[1]; if (grid[row][col].getValue() == 0) { if (grid[row][col].candidates.contains((Object) foundDigit)) { grid[row][col].setValue(foundDigit); updateCellClosure(row,col); return true; } } } } } } return false; } /** * This rule finds all naked pairs/triples, defined * here: http://www.sudokuwiki.org/Naked_Candidates * * @return boolean which is true if the rule succeeded * and the grid is changed or false otherwise */ private boolean ruleNakedSet() {
48
Set<Integer> union2, union3; Integer[] elem, elemA, elemB, elemC; int row, col; // For each row, column or boxID for (int unitSelect = 0; unitSelect < 3; unitSelect++) { for (int i = 0; i < 9; i++) { if (unitSelect == 0) { unit = getRow(i); } else if (unitSelect == 1) { unit = getCol(i); } else if (unitSelect == 2) { unit = getBox(i + 1); } // Puts all empty cell coordinates in freeCells ArrayList<Integer[]> freeCells = new ArrayList<Integer[]>(); for (int j = 0; j < 9; j++) { elem = unit.get(j); row = elem[0]; col = elem[1]; if (grid[row][col].getValue() == 0) { freeCells.add(elem); } } int freeSize = freeCells.size(); for (int a = 0; a < freeSize; a++) { for (int b = a + 1; b < freeSize; b++) { elemA = freeCells.get(a); elemB = freeCells.get(b); union2 = new HashSet<Integer>(grid[elemA[0]][elemA[1]].candidates); union2.addAll(grid[elemB[0]][elemB[1]].candidates); // Check for naked pair if (union2.size() == 2) { // Naked pair found boolean changed = false; for (int n = 0; n < freeSize; n++) { if (n != a && n != b) { elem = freeCells.get(n); changed = grid[elem[0]][elem[1]].candidates.removeAll(union2); } } if (changed) { return true; } } // Check for naked triplet else if (union2.size() == 3) { // Maybe a naked triplet found for (int c = b + 1; c < freeSize; c++) { union3 = new HashSet<Integer>(union2); elemC = freeCells.get(c); union3.addAll(grid[elemC[0]][elemC[1]].candidates);
49
if (union3.size() == 3) { // Naked triplet found boolean changed = false; for (int n = 0; n < freeSize; n++) { if (n != a && n != b && n != c) { elem = freeCells.get(n); changed = grid[elem[0]][elem[1]].candidates.removeAll(union3); } } if (changed) { return true; } } } } } } } } return false; } /** * This rule that finds all naked quads, defined * here: http://www.sudokuwiki.org/Naked_Candidates#NQ * * @return boolean which is true if the rule succeeded * and the grid is changed or false otherwise */ private boolean ruleNakedQuad() { Set<Integer> union4; Integer[] elem, elemA, elemB, elemC, elemD; int row, col; // For each row,col or boxID for (int unitSelect = 0; unitSelect < 3; unitSelect++) { for (int i = 0; i < 9; i++) { if (unitSelect == 0) { unit = getRow(i); } else if (unitSelect == 1) { unit = getCol(i); } else if (unitSelect == 2) { unit = getBox(i + 1); } // Puts all empty cell coordinates in freeCells ArrayList<Integer[]> freeCells = new ArrayList<Integer[]>(); for (int j = 0; j < 9; j++) { elem = unit.get(j); row = elem[0]; col = elem[1]; if (grid[row][col].getValue() == 0) { freeCells.add(elem); } } int freeSize = freeCells.size(); for (int a = 0; a < freeSize; a++) {
50
for (int b = a + 1; b < freeSize; b++) { for (int c = b + 1; c < freeSize; c++) { for (int d = c + 1; d < freeSize; d++) { elemA = freeCells.get(a); elemB = freeCells.get(b); elemC = freeCells.get(c); elemD = freeCells.get(d); union4 = new HashSet<Integer>(grid[elemA[0]][elemA[1]].candidates); union4.addAll(grid[elemB[0]][elemB[1]].candidates); union4.addAll(grid[elemC[0]][elemC[1]].candidates); union4.addAll(grid[elemD[0]][elemD[1]].candidates); // Check for quad if (union4.size() == 4) { // Naked quad found boolean changed = false; for (int n = 0; n < freeSize; n++) { if (n != a && n != b && n != c && n != d) { elem = freeCells.get(n); changed = grid[elem[0]][elem[1]].candidates.removeAll(union4); } } if (changed) { return true; } } } } } } } } return false; } /** * This rule finds all hidden pairs/triples, defined * here: http://www.sudokuwiki.org/Hidden_Candidates * * @return boolean which is true if the rule succeeded * and the grid is changed or false otherwise */ private boolean ruleHiddenSet() { Set<Integer> remainingDigits = new HashSet<Integer>(); int row, col; // For each row, column or boxID for (int unitSelect = 0; unitSelect < 3; unitSelect++) { for (int i = 0; i < 9; i++) { if (unitSelect == 0) { unit = getRow(i); remainingDigits.clear(); } else if (unitSelect == 1) { unit = getCol(i); remainingDigits.clear(); }
51
else if (unitSelect == 2) { unit = getBox(i + 1); remainingDigits.clear(); } // Get remaining digit in this unit for (Integer[] elem:unit) { row = elem[0]; col = elem[1]; remainingDigits.addAll(grid[row][col].candidates); } // Put empty cells coordinates in freeCells ArrayList<Integer[]> freeCells = new ArrayList<Integer[]>(); for (Integer[] elem:unit) { row = elem[0]; col = elem[1]; if (grid[row][col].getValue() == 0) { freeCells.add(elem); } } int freeSize = freeCells.size(); for (int a = 0; a < freeSize; a++) { for (int b = a + 1; b < freeSize; b++) { // Checking two cells at a time Set<Integer> excludingSet = new HashSet<Integer>(remainingDigits); for (int n = 0; n < freeSize; n++) { if (n != a && n != b) { Integer[] elem = freeCells.get(n); row = elem[0]; col = elem[1]; excludingSet.removeAll(grid[row][col].candidates); } } boolean changed = false; // Check if hidden pair if (excludingSet.size() == 2) { // Found hidden pair with the content of excluding set Integer[] elem; for (int k : new int[]{a, b}) { elem = freeCells.get(k); row = elem[0]; col = elem[1]; if (grid[row][col].candidates.retainAll(excludingSet)) { changed = true; } } } if (changed) { return true; } // Check for hidden triples for (int c = b + 1; c < freeSize; c++) { excludingSet = new HashSet<Integer>(remainingDigits);
52
for (int n = 0; n < freeSize; n++) { if (n != a && n != b && n != c) { Integer[] elem = freeCells.get(n); row = elem[0]; col = elem[1]; excludingSet.removeAll(grid[row][col].candidates); } } changed = false; if (excludingSet.size() == 3) { // Found hidden pair with the content of excluding set Integer[] elem; for (int k : new int[]{a, b, c}) { elem = freeCells.get(k); row = elem[0]; col = elem[1]; if (grid[row][col].candidates.retainAll(excludingSet)) { changed = true; } } } if (changed) { return true; } } } } } } return false; } /** * This rule find any box/line reductions, defined * here: http://www.sudokuwiki.org/Intersection_Removal#LBR * * @return boolean which is true if the rule succeeded * and the grid is changed or false otherwise */ private boolean ruleBoxLineReduction() { ArrayList<ArrayList<Integer[]>> unitCandidates = new ArrayList<ArrayList<Integer[]>>(10); int row, col; // For each row and column for (int unitSelect = 0; unitSelect < 2; unitSelect++) { for (int i = 0; i < 9; i++) { if (unitSelect == 0) { unit = getRow(i); unitCandidates.clear(); for (int j = 0; j <= 9; j++) { unitCandidates.add(new ArrayList<Integer[]>()); } } else if (unitSelect == 1) { unit = getCol(i); unitCandidates.clear(); for (int j = 0; j <= 9; j++) { unitCandidates.add(new ArrayList<Integer[]>());
53
} } // Add coordinates according to its candidates for (Integer[] elem:unit) { row = elem[0]; col = elem[1]; if (grid[row][col].getValue() == 0) { for (int cand:grid[row][col].candidates) { unitCandidates.get(cand).add(new Integer[]{row,col}); } } } for (int digit = 1; digit <= 9; digit++) { if (!(unitCandidates.get(digit).isEmpty()) && unitCandidates.get(digit).size() <= 3) { Integer[] cellCoords = unitCandidates.get(digit).get(0); boolean foundMatch = true; int firstRow = cellCoords[0]; int firstCol = cellCoords[1]; int firstBoxID = grid[firstRow][firstCol].boxID; for (int n=1; n<unitCandidates.get(digit).size(); n++) { cellCoords = unitCandidates.get(digit).get(n); int nextRow = cellCoords[0]; int nextCol = cellCoords[1]; int nextBoxID = grid[nextRow][nextCol].boxID; if (firstBoxID != nextBoxID) { foundMatch = false; } } if (foundMatch) { // Maybe found a box line reduction boolean changed = false; ArrayList<Integer[]> boxUnit = getBox(firstBoxID); for (Integer[] boxCellCoords:boxUnit) { boolean inThisUnit = false; int boxRow = boxCellCoords[0]; int boxCol = boxCellCoords[1]; for (Integer[] unitCellCoords:unitCandidates.get(digit)) { int unitRow = unitCellCoords[0]; int unitCol = unitCellCoords[1]; if (boxRow == unitRow && boxCol == unitCol) { inThisUnit = true; } } if (!(inThisUnit)) { if (grid[boxRow][boxCol].candidates.remove(digit)) { changed = true; } } } if (changed) { return true; } }
54
} } } } return false; } /** * This rule find any pointing pairs/triples, defined * here: http://www.sudokuwiki.org/Intersection_Removal * * @return boolean which is true if the rule succeeded * and the grid is changed or false otherwise */ private boolean rulePointingSet() { ArrayList<ArrayList<Integer[]>> unitCandidates = new ArrayList<ArrayList<Integer[]>>(10); int row, col; // For every box for (int boxID = 1; boxID <= 9; boxID++) { unit = getBox(boxID); unitCandidates.clear(); for (int j = 0; j <= 9; j++) { // Initialize the arraylist with arraylists unitCandidates.add(new ArrayList<Integer[]>()); } // Add coordinates according to its candidates for (Integer[] elem:unit) { row = elem[0]; col = elem[1]; if (grid[row][col].getValue()==0) { for (int cand : grid[row][col].candidates) { unitCandidates.get(cand).add(new Integer[]{row,col}); } } } for (int digit = 1; digit <= 9; digit++) { if (0 < unitCandidates.get(digit).size() && unitCandidates.get(digit).size() <= 3) { Integer[] cellCoords = unitCandidates.get(digit).get(0); boolean alignedRow = true; boolean alignedCol = true; int firstRow = cellCoords[0]; int firstCol = cellCoords[1]; // Check if truly aligned on same row or column for (int n = 1; n < unitCandidates.get(digit).size(); n++) { cellCoords = unitCandidates.get(digit).get(n); int nextRow = cellCoords[0]; int nextCol = cellCoords[1]; if (firstRow != nextRow) { alignedRow = false; } if (firstCol != nextCol) { alignedCol = false; } } if (alignedRow || alignedCol) {
55
// Found pointing pair boolean changed = false; ArrayList<Integer[]> fixUnit; if (alignedRow) { fixUnit = getRow(firstRow); } else { fixUnit = getCol(firstCol); } for (Integer[] fixCellCoords : fixUnit) { boolean inThisUnit = false; row = fixCellCoords[0]; col = fixCellCoords[1]; int fixBoxID = grid[row][col].boxID; for (Integer[] unitCellCoords:unitCandidates.get(digit)) { int unitRow = unitCellCoords[0]; int unitCol = unitCellCoords[1]; int unitBoxID = grid[unitRow][unitCol].boxID; if (fixBoxID == unitBoxID) { inThisUnit = true; } } if (!(inThisUnit)) { if (grid[row][col].candidates.remove(digit)) { changed = true; } } } if (changed) { return true; } } } } } return false; } }
BruteforceSolver.java /** * This class tries to solve a sudoku using brute-‐force and * backtracking with the help of Solver. * * @version 1.0 * @author st0le * @modified_by Andreas Broström and Simon Johansson * * @see Solver */ public class BruteforceSolver extends Solver { public int missCount = 0; /** * Initializes the grid, the boxID's and the boxMap. *
56
* @param filename is the name of a sudoku file in the simple * sudoku format (.ss) */ public BruteforceSolver(String filename) { initGrid(filename); initBoxID(); initBoxMap(); } /** * Tries to solve the sudoku by: Finding the first 0 in the * grid and then looking for possible candidates to place there. * If no 0 was found it breaks here (a solution has been found!) * Else it loops through the list of candidates and tries to put * them in the current cell. If something goes wrong the algorithm * backtracks. * * @return boolean which is true if the sudoku was solved and * false otherwise. */ public boolean solve() { int x = 0, y = 0; boolean found = false; for (x = 0; x < 9; x ++) { for (y = 0; y < 9; y++) { if (grid[x][y].getValue() == 0) { found = true; break; } } if (found) break; } if (!found){ return valid(); } boolean digits[] = new boolean[10]; for (int i = 0; i < 9; i++) { digits[grid[x][i].getValue()] = true; digits[grid[i][y].getValue()] = true; } int bx = 3 * (x / 3), by = 3 * (y / 3); for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) digits[grid[bx+i][by+j].getValue()] = true; for(int i = 1 ; i <= 9; i++) { if(!digits[i]) { grid[x][y].setValue(i); if(solve()) return true; grid[x][y].setValue(0); } } missCount++; return false;
57
} }
Testing
run.sh #!/bin/bash while getopts s:p:t: option do case "${option}" in s) SOLVER=${OPTARG};; p) PRINT=${OPTARG};; t) TEST=${OPTARG};; esac done PUZZLES=$(echo puzzles/*.ss | tr " " "\n") for PUZZLE in $PUZZLES do java SudokuSolver $PUZZLE $SOLVER $PRINT $TEST done
run_both_tests.sh #!/bin/bash echo "" > test/human.txt echo "" > test/brute.txt for i in `seq 1 100`; do bash run.sh -‐s brute -‐p computer -‐t both >> test/brute.txt; done for i in `seq 1 100`; do bash run.sh -‐s human -‐p computer -‐t both >> test/human.txt; done
SudokuToExcel.java import java.io.BufferedReader; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.ArrayList; import jxl.Workbook; import jxl.write.Label; import jxl.write.Number; import jxl.write.WritableSheet; import jxl.write.WritableWorkbook; /** * This class reads from the textfiles "human.txt" * and "brute.txt". It prints results to an excel * file called result.xls * * @version 1.0 * @author Andreas Broström
58
* @author Simon Johansson */ public class SudokuToExcel { /** * Reads from file, extracts from string: * id, difficulty, average solving time, * minimum solving time, maximum solving * time and number of misses. * * Prints results to result.xls */ public static void main(String[] args) { try { ArrayList<String> files; ArrayList<Integer> times; int col, row; String[] info; String difficulty, id; String current_id = ""; int time_ms, misses, min_time, max_time; Label label; Number number; WritableWorkbook workbook = Workbook.createWorkbook(new File("result.xls")); WritableSheet sheet = workbook.createSheet("Result", 0); for (int solvertype = 0; solvertype < 2; solvertype++) { //Identifier | Complexitylevel | Avg. solve time | min solve time | max solve time | missCount if (solvertype == 0) { col = 0; files = readFile("human"); label = new Label(0, 0, "Human"); sheet.addCell(label); label = new Label(col++, 2, "ID"); sheet.addCell(label); label = new Label(col++, 2, "Complexity"); sheet.addCell(label); label = new Label(col++, 2, "Average solving time (ms)"); sheet.addCell(label); label = new Label(col++, 2, "Minimum solving time (ms)"); sheet.addCell(label); label = new Label(col++, 2, "Maximum solving time (ms)"); sheet.addCell(label); label = new Label(col++, 2, "Number of misses"); sheet.addCell(label); row = 3; } else { col = 0; files = readFile("brute"); label = new Label(0, 64, "Brute"); sheet.addCell(label); label = new Label(col++, 66, "ID"); sheet.addCell(label); label = new Label(col++, 66, "Complexity"); sheet.addCell(label); label = new Label(col++, 66, "Average solving time (ms)"); sheet.addCell(label); label = new Label(col++, 66, "Minimum solving time (ms)");
59
sheet.addCell(label); label = new Label(col++, 66, "Maximum solving time (ms)"); sheet.addCell(label); label = new Label(col++, 66, "Number of misses"); sheet.addCell(label); row = 67; } // Fix first loop times = new ArrayList<Integer>(); info = files.get(0).split("\\$"); current_id = info[0]; time_ms = Integer.parseInt(info[2]); max_time = time_ms; min_time = time_ms; times.add(max_time); difficulty = info[1]; misses = Integer.parseInt(info[3]); for (String file : files) { info = file.split("\\$"); id = info[0]; if (current_id.equalsIgnoreCase(id)) { if (time_ms > max_time) { max_time = time_ms; } else if (time_ms < min_time) { min_time = time_ms; } times.add(time_ms); } else { //Identifier | Complexitylevel | Avg. solve time | // min solve time | max solve time | missCount col = 0; label = new Label(col++, row, current_id); sheet.addCell(label); label = new Label(col++, row, difficulty); sheet.addCell(label); number = new Number(col++, row, Average(times)); sheet.addCell(number); number = new Number(col++, row, min_time); sheet.addCell(number); number = new Number(col++, row, max_time); sheet.addCell(number); number = new Number(col++, row, misses); sheet.addCell(number); row++; // update new max and min times.clear(); max_time = time_ms; min_time = time_ms; times.add(time_ms); current_id = id; } difficulty = info[1]; time_ms = Integer.parseInt(info[2]);
60
misses = Integer.parseInt(info[3]); } // small fix to include the last sudokus stats col = 0; label = new Label(col++, row, current_id); sheet.addCell(label); label = new Label(col++, row, difficulty); sheet.addCell(label); number = new Number(col++, row, Average(times)); sheet.addCell(number); number = new Number(col++, row, min_time); sheet.addCell(number); number = new Number(col++, row, max_time); sheet.addCell(number); number = new Number(col++, row, misses); sheet.addCell(number); } workbook.write(); workbook.close(); } catch (Exception e) { System.err.println("ERROR:\n"+e.getMessage()); e.printStackTrace(); } } private static float Average(ArrayList<Integer> times) { float sum = 0; for (int time : times) { sum += time; } return sum/times.size(); } /** * Reads a file to an arraylist and returns * (row for row). * * @param filename name of the file to read * @return an arraylist with all rows in the * file as elements. */ public static ArrayList<String> readFile(String filename) { ArrayList<String> files = new ArrayList<String>(); try{ // Open the file that is the first // command line parameter FileInputStream fstream = new FileInputStream("test/" + filename + ".txt"); // Get the object of DataInputStream DataInputStream in = new DataInputStream(fstream); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String strLine; //Read File Line By Line while ((strLine = br.readLine()) != null) { // Print the content on the console files.add(strLine); }
61
//Close the input stream in.close(); }catch (Exception e){//Catch exception if any System.err.println("Error: " + e.getMessage()); } return files; } }
62
Appendix C - Sudoku examples
Ne.se 0903 LÄTT SUDOKU
easy[0903].ss ..8|..5|... 2.4|.1.|... ..1|29.|..3 -‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐ ...|452|67. .5.|.7.|9.. ...|...|... -‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐ .1.|.68|5.. 3..|...|... ...|..3|4..
[12] 1039 SUDOKU MEDIUM
medium[1039].ss ...|.62|... 34.|...|6.. ...|.5.|.9. -‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐ ..2|..1|94. ...|...|.6. ..6|.38|15. -‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐ ...|..7|.8. .5.|9..|... 2..|5..|.39
[12]
63
Harder puzzles Puzzle001 Puzzle001.ss 2..|.57|8.3 7..|...|4.. .43|...|... -‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐ .7.|54.|... 32.|876|.49 ...|.13|.5. -‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐ ...|...|61. ..1|...|..4 4.7|36.|..2 [13] Weekly unsolvable unsolvable.ss 52.|...|6.. 3.7|..8|... ...|.7.|8.. -‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐ 9..|..1|..5 ...|.3.|... 4..|9..|..2 -‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐ ..1|.2.|... ...|6..|4.1 ..9|...|.57 [5]