Saturday, May 10, 2014

Fun and Games with CRV: Sudoku

This week let's mix it up a bit and do something less work-related. Everybody probably knows what Sudoku is, but just in case you don't here's a link to the Wikipedia page.

Sudoku is a pretty popular game that many people play to pass the time. I've played it as well, but I'm not really good at it so I turned to my computer for help. Now, programming a Sudoku solver is probably really difficult, plus I would have no idea where to start. This is why we'll cheat and have the SystemVerilog random number generator solve it for us.

This is the beauty of constraint programming; we don't have to solve problems, we just have to describe them and let our constraint solver do the heavy lifting. While we want to express our problem in the form of constraints, we also want to express everything in an as short and abstract a way as possible. Let's get started!

We'll represent our Sudoku grid as a 9 x 9 array of integers. We'll define all properties (fields and constraints) in a class called sudoku_solver_base. This class will hold all of the rules needed to solve any game of Sudoku, while an actual game will be represented by a sub-class of this class that defines the initial starting point (for example also via constraints or by reading it from a file).

classsudoku_solver_base;randintgrid[9][9];// ...endclass

The first constraint we have is that all elements inside our array must be numbers between 1 and 9, which is pretty easy to express (also note the syntax of a multi-dimensional foreach as this is common gotcha):

The elements on each row must also be unique. This is very easily expressed using SystemVerilog 2012's unique construct:

constraintunique_on_row_c{foreach(grid[i])unique{grid[i]};}

Using unique has saved us from doing double foreach and has made the constraint much more readable.

The complementary of the last constraint is that all elements on a column must also be unique. This is a bit trickier as SystemVerilog doesn't have any way of slicing an array into columns. While we could do some double foreach looping, that wouldn't be as clean as using unique. What we first have to do is construct an auxiliary array that is the transpose of our grid. This requires extra memory but it will help keep our constraints more readable.

We may not have gained very much because we've anyway used a double foreach just to construct the transposed grid, but this is a pretty nifty trick if you need to apply complex constraints on 2D arrays. The idea isn't mine, I "borrowed" it from one of Team Specman's blog posts.

Now we have to concentrate on the 9 sub-grids that make up our Sudoku grid. Like we did with the transposed grid, we want to construct these and use unique on their elements. We can think of these sub-grids as being the elements of a 3x3 matrix. This is how we'll index them:

This was however also not supported as equality constraints cannot be defined on entire arrays, only on individual array elements. This means we have to use brute force and explicitly assign each element (something I would have hoped to avoid):

Here is where the harsh reality of simulator bugs hit me and even though uniqueness constraints on entire arrays are allowed by the LRM, the simulator was encountering an internal error during randomization. While annoying, I thought I could maybe work around it by first storing each sub-grid in a one dimensional array:

Magically, after inserting this constraint, the internal error was gone, even though I hadn't yet removed the array uniqueness constraint. I left this line inside the sample code just to have it working, but be aware that it shouldn't be necessary.

The result I got was also the same as there on the first run. That's right, I got first time right Sudoku!

As always, you can find the full code on the blog's SourceForge repository. If you want to play with it a little more you could also create a file reader for it to read in the starting point of the puzzle.

I hope you enjoyed this little exercise! In a future post I do intend to implement the same thing in e and see if I end up with less code (or more). If you don't want to miss it, feel free to hit the subscribe button.

No comments:

Post a Comment

About

I am a Verification Engineer at Infineon Technologies, where I get the chance to work with both e and SystemVerilog.
I started the Verification Gentleman blog to store solutions to small (and big) problems I've faced in my day to day work. I want to share them with the community in the hope that they may be useful to someone else.