## Let’s (make qaekwy) solve any Sudoku

Welcome to this step-by-step quick-start tutorial on harnessing the power of Qaekwy for constraint programming. In this tutorial, you will swiftly learn how to create a simple Sudoku solver using Qaekwy.

By following this tutorial, you’ll grasp the fundamental concepts of **defining variables**, **constraints**,
and experience the seamless interaction with Qaekwy’s **solver engine**.

Let’s dive in and unravel the world of constraint programming by **creating an efficient Sudoku solver from scratch** !

## Define the grid

Let’s first create the `SudokuGrid`

class that inherits from the `IntegerVariableArray`

class and
is designed to create a Sudoku grid instance.

A Sudoku grid instance is a `9x9`

array variable containing values between `1`

and `9`

.

```
from qaekwy.model.variable.integer import IntegerVariableArray
class SudokuGrid(IntegerVariableArray):
def __init__(self, grid_value=None):
super().__init__(
var_name="grid", # Variable name in the Qaekwy model
length=9*9, # 9x9 array variable
domain_low=1, # Minimum value: 1
domain_high=9 # Maximum value: 9
)
# This variable contains the 9x9 array's initial values.
# This variables is not part of the Qaekwy model itself
# but will be used after to enforce the initial values
# to remain the same, thanks to the constraints that will
# be defined.
self.grid_value = grid_value
```

## Define the problem

Next, we’ll model the Sudoku puzzle-solving problem using the Qaekwy `Modeller`

class. This class is
responsible for adding constraints that ensure the rules of Sudoku are followed and the initial
given values are satisfied.

The Sudoku problem has 3 types of contraints:

- The 3x3 distinct values for the 9 blocks
- The Row distinct values for the 9 rows
- The Col distinct values for the 9 columns

To enforce these rules in our modelling, we will need 3 constraints from Qaekwy:

`ConstraintDistinctSlice`

`ConstraintDistinctRow`

`ConstraintDistinctCol`

In order to fully model the problem, we need to take into account the initial values of the Sudoku grid. To do this, we’re going to require a fourth constraint to ensure that the initial values are preserved:

`RelationalExpression`

The `RelationalExpression`

is used to enfore a relational expression as a constraint over
our grid’s initial values. In our case, any value different from `0`

is identified as constant.

```
from qaekwy.model.modeller import Modeller
from qaekwy.model.constraint.relational import RelationalExpression
from qaekwy.model.constraint.distinct import ConstraintDistinctCol
from qaekwy.model.constraint.distinct import ConstraintDistinctRow
from qaekwy.model.constraint.distinct import ConstraintDistinctSlice
class SudokuProblem(Modeller):
def __init__(self, grid: SudokuGrid):
super().__init__()
self.grid = grid
# Add constraints for distinct values within 3x3 slices
for i in range(0, 9, 3):
for j in range(0, 9, 3):
self.add_constraint(
constraint=ConstraintDistinctSlice(
var_1=self.grid,
size=9,
offset_start_x=i,
offset_end_x=i + 3,
offset_start_y=j,
offset_end_y=j + 3,
)
)
# Add constraints for distinct values within rows and columns
for i in range(0, 9):
# Rows
self.add_constraint(
constraint=ConstraintDistinctRow(
var_1=self.grid,
size=9,
idx=i
)
)
# Columns
self.add_constraint(
constraint=ConstraintDistinctCol(
var_1=self.grid,
size=9,
idx=i
)
)
# Add constraints for initial given values. Here,
# any value different from 0 is identified as constant
# and is put under constraint.
for i in range(0, 81):
if self.grid.grid_value[i] != 0:
self.add_constraint(
constraint=RelationalExpression(
self.grid[i] == self.grid.grid_value[i]
)
)
```

## Add the grid to the model

After modelling the constraints, let’s add the `grid`

variable to the model. We can append
the following instruction to the previous block of code:

```
# Add the Sudoku grid as a variable to the problem
self.add_variable(self.grid)
```

## Define the *searcher*

Qaekwy requires to specify a *searcher*. A searcher is a **methodology used to explore the solution tree**.
In this example, the *Depth-First Search* is perfectly acceptable.

Let’s append the *searcher* setting to our previous block of code:

```
# Set the search strategy to Depth-First Search (DFS)
self.set_searcher(SearcherType.DFS)
```

## What’s next ?

You now have a complete generic definition of the Sudoku problem through the constraints
defined in your `SudokuProblem`

class and your generic grid defined by `SudokuGrid`

.

The next steps are:

- Submit a Sudoku grid
- Call the solver
- Print the solution