StormDoku as a DLL - callable to solve a single sudoku

Programs which generate, solve, and analyze Sudoku puzzles

StormDoku as a DLL - callable to solve a single sudoku

Postby 1to9only » Sat May 26, 2018 5:57 pm

A while back, I downloaded StrmCkr's StormDuko - it's a nice sudoku solver, so I decided to make it into a DLL to be called from a short program I'll create to solve a single sudoku. Here's the how-to process:

See http://forum.enjoysudoku.com/stormduko-t32977.html on downloading the source and freepascal - and many thanks to StrmCtr for making the source of his solver available here - from now on, I'll assume that you've compiled and successfully run the StormDuko program.

[The process described below was done on a 64-bit Windows system building 32-bit programs. The process is slightly different when building 64-bit programs.]

I've renamed my pascal source file stormduko.pas, and replaced the first line of the program:
Hidden Text: Show
Code: Select all
program test;                             //note  popcnt(dword(set variable))); // counts # elments in a set

with this:
Hidden Text: Show
Code: Select all
{ stormduko vrs 6.8.pas by StrmCkr, with modifications by 1to9only }
{ See: http://forum.enjoysudoku.com/stormduko-t32977.html }
{ This program is free software: you can redistribute it and/or modify it under the terms of the GNU general public License. }
{ This program is distributed without any warrenty; without even the implied warrant of Merchantability or fitness for a particular purpose. }
{ See the GNU general public license for more details: https://www.gnu.org/licenses/ }
library stormduko;

And I've replaced the main program procedure, from the '//main body' and 'begin' tags to the last 'end.' in the source with this:
Hidden Text: Show
Code: Select all
function storm_init(): Integer; cdecl;
begin
  peers;
  RCBpeers;
  combo;
  lookupSectorRBC;
  storm_init := 0;
end;
exports
  storm_init;

function storm_solve( sudoku: PChar): PChar; cdecl;
var
  i,j,n: integer;
begin
  initiate;
  grid := sudoku;
  Arange;
  setpm;
  sbrc;
  solve;
  for i := 0 to 80 do
  begin
    n := 0;
    for j := 1 to 9 do
      if j in S[ i] then
        n := j;
    if n in [1..9] then sudoku[ i] := char(48+n);
  end;
  storm_solve := sudoku;
end;
exports
  storm_solve;

end.

Compile with this command:
Code: Select all
ppc386.exe stormduko.pas

A lib file for the dll is required, create the stormduko.def file containing these 3 lines:
Hidden Text: Show
Code: Select all
EXPORTS
storm_init
storm_solve

Create the stormduko.lib file with this command:
Code: Select all
lib.exe /def:stormduko.def /machine:x86 /out:stormduko.lib

This C program (stormsolve.c) shows how to use/call the stormduko.dll to solve a single sudoku:
Hidden Text: Show
Code: Select all
/* stormsolve.c */
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <io.h>

int __cdecl storm_init( void);
char* __cdecl storm_solve( char *psudoku);
#pragma comment( lib, "stormduko")

int main( int argc, char *argv[])
{
   char *psudoku = (char*)0;
   char *psolved = (char*)0;
   char buffer[ 100] = { 0, };
   struct _stat st = { 0, };
   int rc = 0, len = 0, f = 0;

   if ( argc == 2 )
   {
      if ( strlen( argv[1]) == 81 )          // is sudoku line
      {
         psudoku = argv[1];
      }
      else                                   // is sudoku file
      {
         rc = _stat( argv[1], &st);
         len = st.st_size;
         if ( rc == 0 && len >= 81 )
         {
            f = _open( argv[1], _O_RDONLY|_O_BINARY);
            if ( f != -1 )
            {
               rc = _read( f, buffer, 81);   // read 81 bytes only
               rc = close( f);
               buffer[ 81] = 0;              // null terminate after 81 chars
               psudoku = buffer;
            }
         }
         else
         {
            printf( "file '%s' not found!\n", argv[1]);
            exit( 1);
         }
      }
   }

   if ( psudoku != (char*)0 )
   {
      rc = storm_init();
      printf( "%s\n", psudoku);
      psolved = storm_solve( psudoku);
      printf( "%s\n", psolved);
   }
   else
   {
      printf( "nothing to solve!\n");
   }

   return 0;
}

Compile with this command:
Code: Select all
cl.exe stormsolve.c

Run this program, passing it either: 1) a sudoku string (of 81 characters) as argument,
Code: Select all
stormsolve.exe ..5..2..9.83....7.4.987.1.......5.6...7.8.2...3.4.......8.247.3.7....95.3..7..8..

or 2) a filename containing a sudoku (only the first 81 characters are read),
Code: Select all
stormsolve.exe test.sud

The program displays the sudoku solution:
Code: Select all
..5..2..9.83....7.4.987.1.......5.6...7.8.2...3.4.......8.247.3.7....95.3..7..8..
715342689683591472429876135894215367567983241132467598958624713276138954341759826

This python program (stormsolve.py) shows how to use/call the stormduko.dll to solve a single sudoku:
Hidden Text: Show
Code: Select all
#! /usr/bin/python
# stormsolve.py by 1to9only
import sys, os, optparse
from ctypes import *

def main():
   parser = optparse.OptionParser()
   argc, argv = parser.parse_args()

   sudoku = ''

   if len( argv) == 1:
     if len( argv[ 0]) == 81:                   # a sudoku string as argument
        sudoku = argv[ 0]                       # sudoku from argument
     else:                                      # assume a sudoku filename
        isfile = os.path.isfile( argv[ 0])
        if not isfile:
           print( 'file "%s" not found!' % argv[ 0])
           sys.exit( 1)
        f = open( argv[ 0], 'r')
        sudoku = f.read( 81)
        sudoku = sudoku.replace( '\x0a', '')
        sudoku = sudoku.replace( '\x0d', '')
        f.close()
        if len( sudoku) < 81:
           print( 'line: "' + sudoku + '", is too short, length: ' + str( len( sudoku)))
           sys.exit( 2)

   if sudoku != '':
      stormdll = cdll.LoadLibrary( "stormduko.dll")
      rc = stormdll.storm_init()
      print( sudoku)
      psudoku = sudoku.encode( 'ascii')
      psolved = stormdll.storm_solve( psudoku)
      solved = psudoku.decode( 'utf-8')
      print( solved)
   else:
      print( 'nothing to solve!')

   return 0

if __name__ == '__main__':
   sys.exit( main())

Python can be downloaded from https://www.python.org/

Run this python program, passing it either: 1) a sudoku string (of 81 characters) as argument,
Code: Select all
stormsolve.py ..5..2..9.83....7.4.987.1.......5.6...7.8.2...3.4.......8.247.3.7....95.3..7..8..

or 2) a filename containing a sudoku (only the first 81 characters are read),
Code: Select all
stormsolve.py test.sud

The program displays the sudoku solution:
Code: Select all
..5..2..9.83....7.4.987.1.......5.6...7.8.2...3.4.......8.247.3.7....95.3..7..8..
715342689683591472429876135894215367567983241132467598958624713276138954341759826

This program has a 100% success rate with solving the last few recent sudokus posted in the Puzzles section:

Historical Daily 22 - SOLVED
Code: Select all
4.....82...3...1.958....3.4..7.2........76.9..68.....1...7.563.....1.54....3..9.8
479163825623458179581297364397521486145876293268934751914785632832619547756342918

May 25, 2018 - SOLVED
Code: Select all
2....97......1..6....4....3....7..2...83.65...4..8....9....2....6..5......38....1
215639748834715962679428153391574826728396514546281379987142635162953487453867291

Historical Daily 23 - SOLVED
Code: Select all
.19.2..83.538.1.29.28..351..6...219...21....5.41.59.62.......3..349.8.56.86.3.9..
419527683753861429628493517365742198972186345841359762297615834134978256586234971

Historical Daily 24 - SOLVED
Code: Select all
.3.....8...79826..6.......737.2.......1.78..2..86...9.7...9.1...6.1.48......2..36
235761489147982653689345217376219548591478362428653791753896124962134875814527936

Beware! For the extremely difficult puzzle, StormDuko can take several minutes trying all solving techniques before returning with no solution!

This puzzle, which I may have taken from the Patterns Game, ran for 7+ minutes, and no solution was forthcoming!!
Code: Select all
..1.....2..231..4.56...27...3........2....5....7..1.8...6.5...9.1...4...9.....3.4

In the StormDuko thread, StrmCkr's last post mentioned that an update to his pascal source is imminent - I'll update this thread if any changes are required... StrmCkr has now posted version 7.69 of his pascal source code.

Enjoy!

PS. For 64-bit programs (required if you've installed 64-bit python):
Code: Select all
Compile with: ppcrossx64.exe stormduko.pas
Create the lib file with: lib.exe /def:stormduko.def /machine:x64 /out:stormduko.lib
Compile the C program with: cl.exe stormsolve.c

Edit: add remove the files, as I prepare new files (based on version 7.69 source) for update...
Last edited by 1to9only on Thu May 31, 2018 1:30 pm, edited 1 time in total.
User avatar
1to9only
 
Posts: 4177
Joined: 04 April 2018

Re: StormDuko as a DLL - callable to solve a single sudoku

Postby 1to9only » Thu May 31, 2018 5:14 am

Updated: name change to StormDoku, and base is new StormDoku_vrs_7.pas.

Besides the changes (documented in the previous post) applied to StormDoku.pas, I've also added code to the procedure solve to flag the solving methods used to reduce the candidates list (pencil marks). I've added a new call storm_methods to the DLL to retrieve the list of solving methods used.

Array definition for flags of solving methods used:
Code: Select all
mthd: array[1..81] of integer;  {flags for methods used}

Example use of flag of solving method, this one in the procedure for hidden pairs:
Code: Select all
if countpm2<>countpm then mthd[5]:=1;

Example of stormsolve.exe use, calling StormDoku.dll:
Code: Select all
stormsolve.exe ..5..2..9.83....7.4.987.1.......5.6...7.8.2...3.4.......8.247.3.7....95.3..7..8..

The solution is displayed, along with the solving methods used:
Code: Select all
..5..2..9.83....7.4.987.1.......5.6...7.8.2...3.4.......8.247.3.7....95.3..7..8..
715342689683591472429876135894215367567983241132467598958624713276138954341759826
HN BHN XHN HBBNB SKESJSSW TTMT SLLHHHH IXBSTT AATADATF
SS LPP WTT QAAQA MYRWEMMW RRWR W1W1234 WYAURR LHRLDDRI
11 11  1     1 1  11

The solving methods heading is read in 'top-to-bottom' fashion, e.g. HS = hidden singles, NS = naked singles. The full list (in the program solving order) is:
Code: Select all
HS = hidden singles
NS = naked singles
BL = box line reduction
HP = hidden pairs
NP = naked pairs
XW = x-wing
HT = hidden triples
NT = naked triple
HQ = hidden quad
BA = xy-wing
BA = xyz-wing
NQ = naked quad
BA = xyz-wing
SM = skyscrapers - finned and/or sashimi x-wings
KY = 2-string kyte
ER = empty rectangle
SW = sword fish
JE = jelly fish
SM = finned sashimi sword fish
SM = finned sashimi jelly fish
WW = w-wing
TR = fin transport xy-wing
TR = fin transport xyz-wing
MW = m-wing
TR = fin transport xyz-wing
SW = s-wing
L1 = l1-wing
LW = l2-wing
H1 = h1-wing
H2 = h2-wing
H3 = h3-wing
H4 = h4-5-wing
IW = i-w-wing
XY = xy-chain, also hits remote pairs
BA = barns
SU = sue de coq
TR = fin transport bent almost naked sets
TR = fin transport xy-chain, also hits remote pairs
AL = als-xz rule
AH = ahs-xz rule
TR = fin transport als-xz rule
AL = als-xy rule
DD = disjointed distributed subset
AD = almost disjointed distributed subset
TR = fin transport adds
FI = fish finder

If a lib file for the dll is required, create the StormDoku.def file containing these 4 lines:
Code: Select all
EXPORTS
storm_init
storm_solve
storm_methods

The C (stormsolve.c) and python (stormsolve.py) programs have been modified as well.

The program still has a 100% success rate with solving the last few recent sudokus posted in the Puzzles section:

Historical Daily 25 - SOLVED
Code: Select all
..3.742....258...3.......9.7....6..23.5.......6.....7......54...2.1....5...7..96.
853974216692581743174623598781496352345217689269358174916835427427169835538742961
HN BHN XHN HBBNB SKESJSSW TTMT SLLHHHH IXBSTT AATADATF
SS LPP WTT QAAQA MYRWEMMW RRWR W1W1234 WYAURR LHRLDDRI
11 1             1                              11

Historical Daily 26 - SOLVED
Code: Select all
7.9.6...3.1.3..79.6..59..1...7..9..5.61......3......8..4...61..2......7.......5.2
729461853514382796638597214487219365961853427352674981845726139293145678176938542
HN BHN XHN HBBNB SKESJSSW TTMT SLLHHHH IXBSTT AATADATF
SS LPP WTT QAAQA MYRWEMMW RRWR W1W1234 WYAURR LHRLDDRI
11 11  1    1    1

Historical Daily 27 - SOLVED
Code: Select all
6...4..5....8.2.7.347.........78.....1.4....6.....9.32..9........1...4.753.......
682147359195832674347596821926783145713425986458619732879264513261358497534971268
HN BHN XHN HBBNB SKESJSSW TTMT SLLHHHH IXBSTT AATADATF
SS LPP WTT QAAQA MYRWEMMW RRWR W1W1234 WYAURR LHRLDDRI
11 11                                    1

Stormdoku - generated puzzle - SOLVED
Code: Select all
..8.5..79..9.47.2...18.3..........6.........3...3....29..5...478.3.74.9..76..2...
348256179569147328721893456235781964687429513194365782912538647853674291476912835
HN BHN XHN HBBNB SKESJSSW TTMT SLLHHHH IXBSTT AATADATF
SS LPP WTT QAAQA MYRWEMMW RRWR W1W1234 WYAURR LHRLDDRI
11 11   1   1           1 1          1

Historical Daily 28 - SOLVED
Code: Select all
.9.1...565....78...8.4...37.3.9..2...........67.8.2.4595.2.4.71.1.6..5...........
397128456541367892286495137435976218128543769679812345953284671712639584864751923
HN BHN XHN HBBNB SKESJSSW TTMT SLLHHHH IXBSTT AATADATF
SS LPP WTT QAAQA MYRWEMMW RRWR W1W1234 WYAURR LHRLDDRI
11 11       1    1        1

Warning! As more solving methods have been added, StormDoku now takes even longer (trying all solving techniques) to solve sudokus!

This puzzle, maybe taken from the Patterns Game, ran for 15+ minutes, and still no solution was forthcoming!!
Code: Select all
..1.....2..231..4.56...27...3........2....5....7..1.8...6.5...9.1...4...9.....3.4

The source files.
StormDoku.pas.zip (42,951 bytes)
stormsolve.c.zip (825 bytes)
stormsolve.py.zip (811 bytes)
User avatar
1to9only
 
Posts: 4177
Joined: 04 April 2018


Return to Software