Some notes:

Remember that you have to work in pairs.

**Only groups of size 2 are allowed to submit!**Submissions by only 1 person or 3 or more persons will not be accepted by the Fire system.When you are done, please submit your solution using the Fire system.

If you are stuck on this assignment, please read the page on Getting Help carefully!

## Lab Assignment 3: Sudoku

In this Lab Assignment, you will design a Haskell program that will be able to solve Sudokus, a popular logical puzzle originating from Japan.### Assignments and Deadlines

There are 6 regular assignments as part of this Lab: A, B, C, D, E, and F. The lab consists (again) of two parts.For submission, assignments A, B, C and D are called **Lab 3A**.

Assignments E and F are called **Lab 3B**.

Deadlines for each of these parts are given on the home page. There are also extra assignments. You can choose freely whether to do one of these. Those are just for fun.

### Hints

Some assignments have hints. Often, these involve particular standard Haskell functions that you could use. Some of these functions are defined in modules that you have to import yourself explicitly. You can use the following resources to find more information about those functions:Hoogle, the library function search engine

A Tour of the Haskell Prelude shows standard Haskell functions that you get without importing any module.

Haskell Hierarchical Libraries, all the library modules, included with the latest version of GHC.

## Sudokus

Sudoku is a logic puzzle originating in Japan and has caught on in popularity also in the West in recent years. Many newspapers publish a daily Sudoku puzzle for the readers to solve.A Sudoku puzzle consists of a 9x9 grid. Some of the cells in the grid have digits (from 1 to 9), others are blank. The objective of the puzzle is to fill in the blank cells with digits from 1 to 9, in such a way that every row, every column and every 3x3 block has exactly one occurrence of each digit 1 to 9.

3 | 6 | 7 | 1 | 2 | ||||

5 | 1 | 8 | ||||||

9 | 2 | 4 | 7 | |||||

1 | 3 | 2 | 8 | |||||

4 | 5 | 2 | 9 | |||||

2 | 7 | 4 | 6 | |||||

5 | 3 | 8 | 9 | |||||

8 | 3 | 6 | ||||||

7 | 6 | 9 | 4 | 3 |

3 | 6 | 4 | 8 | 7 | 1 | 2 | 9 | 5 |
---|---|---|---|---|---|---|---|---|

7 | 5 | 2 | 9 | 3 | 6 | 1 | 8 | 4 |

8 | 1 | 9 | 2 | 5 | 4 | 7 | 3 | 6 |

5 | 9 | 6 | 7 | 1 | 3 | 4 | 2 | 8 |

4 | 3 | 1 | 5 | 8 | 2 | 6 | 7 | 9 |

2 | 7 | 8 | 4 | 6 | 9 | 3 | 5 | 1 |

6 | 4 | 5 | 3 | 2 | 8 | 9 | 1 | 7 |

9 | 8 | 3 | 1 | 4 | 7 | 5 | 6 | 2 |

1 | 2 | 7 | 6 | 9 | 5 | 8 | 4 | 3 |

In this lab assignment, you will write a Haskell program that can read in a Sudoku puzzle and solve it.

### More Information

If you want to read more about Sudokus, here are a few links:The Daily Sudoku has examples and explanations.

sudoku.com has examples and explanations.

sudoku.com.au has sudoku puzzles that you can solve online.

## Modelling Sudokus

To implement a Sudoku-solving program, we need to come up with a way of modelling Sudokus. A Sudoku is a matrix of digits or blanks. The natural way of modelling a matrix is as a list of lists. The outer list represents all the rows, and the elements of the list are the elements of each row. Digits or blanks can be represented by using the Haskell`Maybe`

type. Digits are
simply represented by `Int`

.
Summing up, a natural way to represent Sudokus is using the following Haskell datatype:

Since it is convenient to have a function that extracts the actual rows from the Sudoku, we actually use the following equivalent datatype definition:dataSudoku=Sudoku[[MaybeInt]]

For example, the above Sudoku puzzle has the following representation in Haskell:dataSudoku=Sudoku{rows::[[MaybeInt]]}

or, written in a more compact way:example::Sudokuexample=Sudoku[ [Just3,Just6,Nothing,Nothing,Just7,Just1,Just2,Nothing,Nothing] , [Nothing,Just5,Nothing,Nothing,Nothing,Nothing,Just1,Just8,Nothing] , [Nothing,Nothing,Just9,Just2,Nothing,Just4,Just7,Nothing,Nothing] , [Nothing,Nothing,Nothing,Nothing,Just1,Just3,Nothing,Just2,Just8] , [Just4,Nothing,Nothing,Just5,Nothing,Just2,Nothing,Nothing,Just9] , [Just2,Just7,Nothing,Just4,Just6,Nothing,Nothing,Nothing,Nothing] , [Nothing,Nothing,Just5,Just3,Nothing,Just8,Just9,Nothing,Nothing] , [Nothing,Just8,Just3,Nothing,Nothing,Nothing,Nothing,Just6,Nothing] , [Nothing,Nothing,Just7,Just6,Just9,Nothing,Nothing,Just4,Just3] ]

Now, a number of assignments follows, which will lead you step-by-step towards an implementation of a Sudoku-solver.example::Sudokuexample=Sudoku[[j3,j6,n,n,j7,j1,j2,n,n], [n,j5,n,n,n,n,j1,j8,n], [n,n,j9,j2,n,j4,j7,n,n], [n,n,n,n,j1,j3,n,j2,j8], [j4,n,n,j5,n,j2,n,n,j9], [j2,j7,n,j4,j6,n,n,n,n], [n,n,j5,j3,n,j8,j9,n,n], [n,j8,j3,n,n,n,n,j6,n], [n,n,j7,j6,j9,n,n,j4,j3]]wheren=Nothingj=Just

## Some Basic Sudoku Functions

To warm up, we start with a number of basic functions on Sudukos.### Assignment A

**A1.**Implement a function

that represents a Sudoku that only contains blank cells (this means that no digits are present).allBlankSudoku::Sudoku

Do not use copy-and-paste programming here! Your definition does not need to be longer than a few short lines.

**A2.**The Sudoku type we have defined allows for more things than Sudokus. For example, there is nothing in the type definition that says that a Sudoku has 9 rows and 9 columns, or that digits need to lie between 1 and 9. Implement a function

that checks if all such extra conditions are met by the given Sudoku.isSudoku::Sudoku->Bool

Examples:

`isSudoku (Sudoku [])`

False`isSudoku allBlankSudoku`

True`isSudoku example`

True`isSudoku (Sudoku (tail (rows example)))`

False

*Note:*It is possible to define types that more precisely captures well-formed sudokus, thus making the

`isSudoku`

function superfluous
and preventing bugs involving ill-formed sudokus from happening.
If you want to explore this possibility,
see the the extra assignment **Ä1**.

**A3.**Our job is to solve Sudokus. So, it would be handy to know when a Sudoku is solved or not. To begin with, we need a way to check if a Sudoku has been completely filled, i.e. there are no blank cells left. Implement the following function:

Note that we do not check here if the Sudoku is aisFilled::Sudoku->Bool

*valid*solution; we will do this later. This means that

`isFilled`

returns
**True**

for any Sudoku without blanks (even
if there is a row that contains the same digit twice, for example).
### Hints

To implement the above, use list comprehensions! Also, the following standard Haskell functions might come in handy:To help you get started, here is a file that you can use:replicate::Int->a->[a]length::[a]->Intand::[Bool]->Bool

- Sudoku.hs, with some definitions that help you get going with Assignments A, B and C.

## Reading and Printing Sudokus

Next, we need to have a way of representing Sudokus in a file. In that way, our program can read Sudokus from a file, and it is easy for us to create and store several Sudoku puzzles.The following is an example text-representation that we will use in this assignment. It actually represents the example above.

36..712.. .5....18. ..92.47.. ....13.28 4..5.2..9 27.46.... ..53.89.. .83....6. ..769..43There are 9 lines of text in this representation, each corresponding to a row. Each line contains 9 characters. A digit 1 -- 9 represents a filled cell, and a period (.) represents a blank cell.

### Assignment B

**B1.**Implement a function:

that, given a Sudoku, creates instructions to print the Sudoku on the screen, using the format shown above.printSudoku::Sudoku->IO()

Example:

`printSudoku allBlankSudoku`

.........
.........
.........
.........
.........
.........
.........
.........
.........

`printSudoku example`

36..712..
.5....18.
..92.47..
....13.28
4..5.2..9
27.46....
..53.89..
.83....6.
..769..43

**B2.**Implement a function:

that, given a filename, creates instructions that read the Sudoku from the file, and deliver it as the result of the instructions. You may decide yourself what to do when the file does not contain a representation of a Sudoku.readSudoku::FilePath->IOSudoku

Examples:

`sud <- readSudoku "example.sud"`

`printSudoku sud`

36..712.. .5....18. ..92.47.. ....13.28 4..5.2..9 27.46.... ..53.89.. .83....6. ..769..43`readSudoku "Sudoku.hs"`

Program error: Not a Sudoku!

### Hints

To implement the above, you will need to be able to convert between characters
(type `Char`

) and digits/integers (type `Int`

). The standard functions
`show`

and `digitToInt`

(import the module `Data.Char`

) will come in handy
here.

Here are some more functions that might come in handy:

Here are some example Sudoku-files that you can download and use:digitToInt::Char->IntputStr::String->IO()putStrLn::String->IO()readFile::FilePath->IOStringlines::String->[String]unlines::[String]->String

- example.sud, containing the above example.
- sudokus.zip, a ZIPped collection of sudokus, both easy and hard ones. The easy ones should all be solvable by your final program within minutes; the hard ones will probably take a very long time (unless you do extra Assignment X and/or Y)!.

## Generating Sudokus as Test Data

Finally, we need to be able to test properties about the functions related to our Sudokus. In order to do so, QuickCheck needs to be able to generate arbitrary Sudokus.
Let us split this problem into a number of smaller problems. First, we need to
know how to generate arbitrary cell values (of type `Maybe Int`

). Then,
we need to know how to generate 81 such cells, and compose them all into a
Sudoku.

### Assignment C

**C1.**Implement a function:

that, contains instructions for generating a Sudoku cell. You have to think about the following:cell::Gen(MaybeInt)

- Cells either contain a digit between 1 and 9 (for example
`Just 3`

) or are empty (`Nothing`

), - We would like our generated Sudokus to resemble realistic Sudoku puzzles. Therefore, the distribution should be around 10% probability non-empty cells vs. 90% probability for empty cells. (This is not something strict; you can play around with this if you like.)

Example:

`sample cell`

Just 3
Nothing
Nothing
Just 7
Nothing

**C2.**Make Sudokus an instance of the class Arbitrary.

**instance** **Arbitrary** **Sudoku** **where**

...

We have already done this for you in the file
Sudoku.hs.
**C3.**Define a property

that expresses that each generated Sudoku actually is a Sudoku according to Assignment A2. Also use QuickCheck to check that the property actually holds for all Sudokus that are generated.prop_Sudoku::Sudoku->Bool

### Hints

Here are some functions from`Test.QuickCheck`

that might come in handy:
You might want to take a look at the lecture notes and example code on test data generation.sample::Showa=>Gena->IO()choose::Randoma=>(a,a)->Genaelements::[a]->Genafrequency::[(Int,Gena)]->Genasequence::[Gena]->Gen[a]vectorOf::Int->Gena->Gen[a]

## Rows, Columns, Blocks

Now, we are going to think about what actually constitutes a valid solution of a Sudoku. There are three constraints that a valid solution has to fulfill:- No row can contain the same digit twice;
- No column can contain the same digit twice;
- No 3x3 block can contain the same digit twice.

*block*; a block is either a row or a column or a 3x3 block. A block therefore contains 9 cells:

We are going to define a function that checks if a Sudoku is not violating any of the above constraints, by checking that none of the blocks violate those constraints.typeBlock=[MaybeInt]

### Assignment D

**D1.**Implement a function:

that, given a block, checks if that block does not contain the same digit twice.isOkayBlock::Block->Bool

Examples:

`isOkayBlock [Just 1, Just 7, Nothing, Nothing, Just 3, Nothing, Nothing, Nothing, Just 2]`

True`isOkayBlock [Just 1, Just 7, Nothing, Just 7, Just 3, Nothing, Nothing, Nothing, Just 2]`

False

**D2.**Implement a function:

that, given a Sudoku, creates a list of all blocks of that Sudoku. This means:blocks::Sudoku->[Block]

- 9 rows,
- 9 columns,
- 9 3x3 blocks.

Also add a property that states that, for each Sudoku, there are 3*9 blocks, and each block has exactly 9 cells.

prop_blocks_lengths::...

**D3.**Now, implement a function:

that, given a Sudoku, checks that all rows, colums and 3x3 blocks do not contain the same digit twice.isOkay::Sudoku->Bool

Examples:

`isOkay allBlankSudoku`

True`sud <- readSudoku "example.sud"`

`isOkay sud`

True

### Hints

Here are some functions that might come in handy:Note that some of the above functions only appear when younub::Eqa=>[a]->[a]transpose::[[a]]->[[a]]take::Int->[a]->[a]drop::Int->[a]->[a]

**import** **Data.List**

.
You might want to take a look at the exercises and answers on lists and list comprehensions.

## Positions and Finding Blanks

We are getting closer to the final solving function. Let us start thinking about how such a function would work.Given a Sudoku, if there are no blanks left in the Sudoku, we are done. Otherwise, there is at least one blank cell that needs to be filled in somehow. We are going to write functions to find and manipulate blank cells.

It is quite natural to start to talk about *positions*. A position is a
coordinate that identifies a cell in the Sudoku. Here is a way of modelling
coordinates:

We use positions as indicating first the row and then the column. For example, the position (3,5) denotes the 5th cell in the 3rd row.typePos=(Int,Int)

Note: It is common in programming languages to start counting at 0! Therefore, the position that indicates the upper left corner is (0,0), and the position indicating the lower right corner is (8,8).

### Assignment E

**E1.**Implement a function:

that, given a Sudoku returns a list of the positions in the Sudoku that are still blank. You may decide on the order in which the positions appear.blanks::Sudoku->[Pos]

Examples:

`length (blanks allBlankSudoku) == 9*9`

True`blanks example`

[(0,2),(0,3),(0,7),(0,8),(1,0),(1,2),(1,3),(1,4),(1,5),(1,8),(2,0),(2,1),(2,4),(2,7),(2,8),(3,0),(3,1),(3,2),(3,3),(3,6),(4,1),(4,2),(4,4),(4,6),(4,7),(5,2),(5,5),(5,6),(5,7),(5,8),(6,0),(6,1),(6,4),(6,7),(6,8),(7,0),(7,3),(7,4),(7,5),(7,6),(7,8),(8,0),(8,1),(8,5),(8,6)]

In addition, write a property that states that all cells in the blanks list are actually blank.

prop_blanks_allBlank::...

**E2.**Implement a function:

that, given a list, and a tuple containing an index in the list and a new value, updates the given list with the new value at the given index.(!!=)::[a]->(Int,a)->[a]

Examples:

`['a','b','c','d'] !!= (1,'X')`

['a','X','c','d']`["p","qq","rrr"] !!= (0,"bepa")`

["bepa","qq","rrr"]

Also write a property that state(s) the expected properties of this function. Think about what can go wrong!

prop_bangBangEquals_correct::...

**E3.**Implement a function:

that, given a Sudoku, a position, and a new cell value, updates the given Sudoku at the given position with the new value.update::Sudoku->Pos->MaybeInt->Sudoku

Example:

`printSudoku (update allBlankSudoku (1,3) (Just 5))`

.........
...5.....
.........
.........
.........
.........
.........
.........
.........

Also write a property that checks that the updated position really has gotten the new value.

prop_update_updated::...

**E4.**Implement a function:

that, given a Sudoku, and a blank position, determines which numbers could be legally written into that position.candidates::Sudoku->Pos->[Int]

Example:

`candidates example (0,2)`

[4,8]`candidates allBlankSudoku (8,8)`

[1,2,3,4,5,6,7,8,9]

In addition, write a property that relates the function candidates with
the functions `blanks`

, `update`

,
`isSudoku`

, and `isOkay`

.
(This property can be very useful to understand how to solve Sudokus!)

prop_candidates_correct::...

### Hints

There is a standard function`(!!)`

in Haskell for getting a specific
element from a list. It starts indexing at 0, so for example to get the
first element from a list xs, you can use xs !! 0.
We usually use the standard function `zip`

to pair up elements in a list
with their corresponding index. Example:

`["apa","bepa","cepa"] `zip` [1..3]`

[("apa",1),("bepa",2),("cepa",3)]

This, in combination with list comprehensions, should be very useful for this
assignment!
When testing a property that is polymorphic (meaning that it has type variables in its type), you need to add a type signature that picks an arbitrary type. For example, when testing properties for the function (!!=), which works for lists of any type, you have to fix the type when testing, for example lists of Integers. Do this by adding a type signature to your properties.

Here are some more useful functions:

head::[a]->a(!!)::[a]->Int->azip::[a]->[b]->[(a,b)]

## Solving Sudokus

Finally, we have all the bits in place to attack our main problem: Solving a given Sudoku.Our objective is to define a Haskell function

The basic idea is as follows. Function solve must first check that its argument is not already a bad Sudoku. This means that (1) it represents a 9x9 sudoku, (2) it has no blocks (rows, columns, 3x3 blocks) that contain the same digit twice. We will only do this check once. If the argument is bad then solve must returnsolve::Sudoku->MaybeSudoku

`Nothing`

Now if we have such a Sudoku `sud`

that we would like to solve, we give it to a recursive helper function `solve'`

.

The `solve'`

function must
consider all the blanks in `sud`

. If this list is empty then
by (1) and (2) above we are done, and the answer of `solve'`

(and hence `solve`

)
must be `Just sud`

.

Otherwise there is at least one blank position. We choose one of them.
For this blank position we
we try to *recursively* solve `sud`

, once for each possible candidate; in each recursive case we update the
blank cell with a candidate. The first recursive attempt that
does not give `Nothing`

provides our solution. But if none of the recursive attempts succeed, we return
`Nothing`

.

This method of problem solving is called backtracking.

### Assignment F

**F1.**Implement a function:

using the above idea.solve::Sudoku->MaybeSudoku

Examples:

`printSudoku (fromJust (solve allBlankSudoku))`

123456789
456789123
789123456
214365897
365897214
897214365
531642978
642978531
978531642

`sud <- readSudoku "example.sud"`

`printSudoku (fromJust (solve sud))`

364871295 752936184 819254736 596713428 431582679 278469351 645328917 983147562 127695843`sud <- readSudoku "impossible.sud"`

`solve sud`

Nothing

(In the above examples, we use the standard function `fromJust`

from the library

.)
**Data.Maybe**

**F2.**For your own convenience, define a function:

that produces instructions for reading the Sudoku from the given file, solving it, and printing the answer.readAndSolve::FilePath->IO()

Examples:

`readAndSolve "example.sud"`

364871295 752936184 819254736 596713428 431582679 278469351 645328917 983147562 127695843`readAndSolve "impossible.sud"`

(no solution)

**F3.**Implement a function:

that checks, given two Sudokus, whether the first one is a solution (i.e. all blocks are okay, there are no blanks), and also whether the first one is a solution of the second one (i.e. all digits in the second sudoku are maintained in the first one).isSolutionOf::Sudoku->Sudoku->Bool

Examples:

`fromJust (solve allBlankSudoku) `isSolutionOf` allBlankSudoku`

True`allBlankSudoku `isSolutionOf` allBlankSudoku`

False`fromJust (solve allBlankSudoku) `isSolutionOf` example`

False

**F4.**Define a property:

that says that the functionprop_SolveSound::Sudoku->Property

`solve`

is *sound*. Soundness means that every supposed solution produced by

`solve`

actually is a valid solution of the original problem.
### Hints

All the work we did in the assignments A -- E should be used in order to implement the function`solve`

.
QuickChecking the property `prop_SolveSound`

will probably take a long time. Be patient!
Alternatively, there are a number of things you can do about this.

You can test on fewer examples (using the QuickCheck function

`quickCheckWith`

). You can for example define:

and then write`fewerChecks``prop`**=**`quickCheckWith``stdArgs`{`maxSuccess`**=**30 }`prop``fewerChecks prop_SolveSound`

when you want to QuickCheck the property.You can also generate Sudokus with a different probability distribution. Try increasing the amount of digits in an arbitrary Sudoku by fiddling with the frequencies in the

`cell`

function from Assignment C1 and see what happens.Instead of running interpreted code inside GHCi, you can compile your program to an exacutable file containing optimised native machine code, and run that:

`ghc -O Sudoku.hs`

[1 of 1] Compiling Main ( Sudoku.hs, Sudoku.o ) Linking Sudoku ...`./Sudoku`

…*Note:*You need to define a`main`

function that runs the test you are interested in.

It is okay if you do not find a completely satisfactory solution to this issue.

Here are some useful functions:

listToMaybe::[a]->MaybeacatMaybes::[Maybea]->[a]fromJust::Maybea->a-- not recommended

Note that `fromJust`

is a *partial function* that can
cause your program to crash if used incorrectly
(i.e. if your program tries to compute

).
The function `fromJust` **Nothing**`head`

is also a partial function: it is not defined
for empty lists,

crashes. Using other
library functions or pattern matching instead is more likely to result in code
that is both elegant and correct.
`head` []

Here is an example of an impossible Sudoku:

## Extra Assignments (for fun)

Just for fun. You can choose freely whether to do 0, 1 or more of these. Don't expect us to spend time grading these however. There are no perfect, pre-defined answers here.## Submission

Submit your solutions using the Fire system.Your submission should consist of the following file:

**Sudoku.hs**, containing your solution. It should contain enough comments to understand what is going on.

Before you submit your code,
spend some time on **cleaning up** your code; make it simpler,
remove unneccessary things, etc.
We will reject your solution if it is not clean. Clean code:

- Does not have long lines (< 78 characters)
- Has a consistent layout
- Has type signatures for all top-level functions
- Has good comments
- Has no junk (junk is unused code, commented code, unneccessary comments)
- Has no overly complicated function definitions
- Does not contain any repetitive code (copy-and-paste programming)

This year we have added Automatic checks in Fire that will run when you submit your answers. The tests include runing hlint and perhaps testing some of your functions with QuickCheck. The purpose of this is to give you some quick feedback and to help speed up the grading process. If the feedback you get is not helpful, you can safely ignore it.

Good Luck!