212 lines
7.1 KiB
Markdown
212 lines
7.1 KiB
Markdown
# PDP-10 simple solver for Sudokus
|
|
|
|
## What is a sudoku?
|
|
|
|
[Sudoku](https://en.wikipedia.org/wiki/Sudoku) is a logic based number-placement
|
|
puzzle game originally from Japan. The word *sudoku* literally means
|
|
*digit-single*. The objecttive is to fill a 9x9 grid with numbers between 1 and
|
|
9, without having a number repeat in a row, column or a 3x3 subgrid. Normally,
|
|
there are a few numbers preset. In a regular sudoku, the pre-filled numbers make
|
|
sure, that exactly one solution for the puzzle exists.
|
|
|
|
The number of possible valid solutions to a sudoku is finite. In a classical
|
|
Sudoku, there are 6,670,903,752,021,072,936,960 possible valid grids. A number
|
|
that reduces to 5,472,730,538 essentially different solutions. The Sudokus you
|
|
will find in a modern day puzzle book, are always one of those roughly 5
|
|
billion, in order to guarantee a solution.
|
|
|
|
### Rules
|
|
|
|
A Sudoku is presented in a 9x9-grid:
|
|
|
|
```
|
|
||===|===|===||===|===|===||===|===|===||
|
|
|| | | 2 || 9 | | 3 || 8 | | ||
|
|
||---|---|---||---|---|---||---|---|---||
|
|
|| | | || 4 | 6 | 8 || | | ||
|
|
||---|---|---||---|---|---||---|---|---||
|
|
|| 4 | | || | | || | | 1 ||
|
|
||===|===|===||===|===|===||===|===|===||
|
|
|| 5 | 6 | || | | || | 4 | 9 ||
|
|
||---|---|---||---|---|---||---|---|---||
|
|
|| | 2 | || | 5 | || | 7 | ||
|
|
||---|---|---||---|---|---||---|---|---||
|
|
|| 7 | 8 | || | | || | 1 | 3 ||
|
|
||===|===|===||===|===|===||===|===|===||
|
|
|| 2 | | || | | || | | 7 ||
|
|
||---|---|---||---|---|---||---|---|---||
|
|
|| | | || 3 | 8 | 4 || | | ||
|
|
||---|---|---||---|---|---||---|---|---||
|
|
|| | | 6 || 2 | | 1 || 3 | | ||
|
|
||===|===|===||===|===|===||===|===|===||
|
|
```
|
|
|
|
The rules are the following:
|
|
|
|
- Every row must contain every number between 1 and 9 exactly once.
|
|
- Every vertical column must contain all the numbers between 1 and 9 exactly
|
|
once.
|
|
- Every 3x3 sub-grid must contain every number between 1 and 9 exactly once.
|
|
|
|
## Solving Sudokus programatically
|
|
|
|
### Backtracking (naïve approach)
|
|
|
|
Solving a Sudoku can be achieved in serveral ways. The most simple algorithm, is
|
|
a backtracking algorithm. With this a sudoku can be resolved basically by
|
|
brute-forcing it. In this case, we walk through each cell of the grid
|
|
recurseivly, and add a number. Then we check, if the grid is still valid. If it
|
|
is, we recursivly move on to the next column. If we find a number that doesn't
|
|
match, we return to the previous incarnation, and try another number. In order
|
|
to check, if our grid is still valid, we can use simple function:
|
|
|
|
```C
|
|
// Function to check, if it is save to place num at mat[row][col]
|
|
int isSafe(int[][] *mat, int row, int col, int num) {
|
|
// Check if num exists in the row
|
|
for (int x = 0; x <= 8; x++) {
|
|
if (*mat[row][x] == num) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Check, if num exists in column
|
|
for (int x = 0; x < 8; x++) {
|
|
if (*mat[x][col]) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Check if num exists in the 3x3 sub matrix
|
|
int startRow = row - (row % 3);
|
|
int startCol = col - (col % 3);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
for (int j = 0; j < 3; j++ {
|
|
if (*mat[i + startRow][j + startCol] == num) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
```
|
|
|
|
The only bit in this code, not straight forward, is the last part. By taking the
|
|
modulus of the current row and column, we can find the top left element in each
|
|
sub-grid, and adding the according number to it, we can then walk through.
|
|
|
|
The solving algorithm itself, is surprisingly simple:
|
|
|
|
```C
|
|
// Function to solve the sudoku problem by backtracking.
|
|
int solveSudokuRec(int[][] *mat, int row, int col) {
|
|
int n = sizeof(*mat);
|
|
|
|
// base case: Reached the nth column of the last row. We're done.
|
|
if (row == n - 1 && col == n) {
|
|
return 1;
|
|
}
|
|
|
|
// if last column of a row, go to next row
|
|
if (col == n) {
|
|
row++;
|
|
col=0;
|
|
}
|
|
|
|
// if cell is already occupied, move on
|
|
if (*mat[row][col] != 0) {
|
|
return solveSudokuRec(mat, row, col + 1);
|
|
}
|
|
|
|
for (int num = 1; num <= n; num++) {
|
|
|
|
// if it is safe to place num at current position, do so.
|
|
if (isSafe(mat, row, col, num)) {
|
|
*mat[row][col] = num;
|
|
if (solveSudokuRec(mat, row, col + 1)) {
|
|
return 1;
|
|
}
|
|
|
|
*mat[row][col] = 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
And that's it.
|
|
|
|
## Using the PDP-10 to solve the Sudoku
|
|
|
|
In order to run this program, you need a working PDP-10 simulator (or, if you
|
|
are a very lucky person,the real thing). You can either use the
|
|
[PiDP-10 kit](https://obsolescence.wixsite.com/obsolescence/pidp10), or a
|
|
Raspberry Pi 5 to run it on. The PiDP-10 can be run on any Raspberry 5 computer,
|
|
even without the front panel, but having the front panel is fancier. You can
|
|
also run [simh](https://opensimh.org/) on a PC, but that would only be half the
|
|
fun.
|
|
|
|
The PiDP-10 shold be booted into ITS, as this is the OS, this project is running
|
|
against. Now the only thing you need to do, is copy the source code files into
|
|
the file system of your PiDP-10. You can do this by using the `mlftp` tool,
|
|
installed on the machine your simulator runs on. You clone the repository and
|
|
run the following commands from the command line:
|
|
|
|
```bash
|
|
/opt/pidp10/bin/mlftp -w ITS "SUDOKU 1 JALI;" ./src/sudoku.s
|
|
/opt/pidp10/bin/mlftp -w ITS "PUZZL1 SUDOKU JALI;" ./src/puzzle_1.sudoku
|
|
```
|
|
|
|
This will copy the source code file, and the example puzzle file onto the
|
|
PiDP-10. Remmber to change the directory name in the ITS path form `JALI` to the
|
|
directory you want to copy to.
|
|
|
|
You can now edit both files in EMACS on the PiDP-10. However, if you prefer to
|
|
edit your code in a somewhat more modern environment (your favourite IDE, for
|
|
example), consider installing the
|
|
[git-monitor](https://gitea.orca-central.de/jali/PiDP10Services) on your
|
|
PiDP-10's host system. The monitor is a service written in
|
|
[bash](https://www.gnu.org/software/bash/), that will clone your project
|
|
repositories and then monitor a given branch for new commits. On new commits it
|
|
pulls them, and runs the script `.git-monitor_after.sh`, which in turn, contains
|
|
the upload commands for ITS. This way, you can automatically update your source
|
|
code in ITS, even when you are working on a modern machine.
|
|
|
|
## Building and running
|
|
|
|
Log into ITS with your developers user name by typing `USERNAME$U` (where `$` is
|
|
the ESC-Key), If you are a *luser*: Type `:LOGIN USERNAME` and swap `USERNAME`
|
|
for your *actual* username. Confirm with `Enter`.
|
|
|
|
Now you can build the sudoku solver by invoking MIDAS in DDT:
|
|
|
|
```
|
|
:MIDAS TS SUDOKU_SUDOKU
|
|
```
|
|
|
|
This will instruct MIDAS to create a relocatable binary (TS) from the latest
|
|
version of the `SUDOKU` file, and store it in a file named `TS SUDOKU` in the
|
|
current directory. If this succeded, listing the files with `:LISTF` (or
|
|
`CTRL+F`, if you're not a turist), should look something like this:
|
|
|
|
```
|
|
KA JALI
|
|
FREE BLOCKS #2=154 #3=169 #0=156 #1=171
|
|
1 PUZZL1 SUDOKU 1 ! 9/21/2025 22:02:04
|
|
3 SUDOKU 1 1 ! 9/21/2025 22:02:02
|
|
1 TS SUDOKU 1 ! 9/21/2025 23:25:09
|
|
```
|
|
|
|
To invoke the solver run it with:
|
|
|
|
```
|
|
:SUDOKU PUZZL1 SUDOKU
|
|
```
|
|
|
|
This will pass the file `PUZZL1 SUDOKU` to the program, and start to solve it.
|
|
|
|
The result will be printed on the screen (once the project is finished).
|