Post Reply 
Sudoku editor/Solver
12-04-2014, 07:06 AM (This post was last modified: 02-01-2015 09:25 PM by lenborje.)
Post: #3
RE: Sudoku editor/Solver
(12-03-2014 07:10 PM)Mic Wrote:  I get an empty program when I open it... ?

Strange, as far as I can see the zip is correct. Here's the code (rather big, you'll have to scroll a lot):

Code:
#pragma mode( separator(.,;) integer(h32) )

// Sudoku editor/solver by Lennart Börjeson 2015-01-31
//
// The solution strategies are my own and can surely be improved.
//
// The basic idea is to represent a sudoku grid by a NxN matrix, where
// a positive number is a known cell, and a negative number a bitmask
// of possible digits.
//
// E.g. a 9x9 grid is initialised with all cells set to -1022, since
// 1022 == #1111111110b, i.e. bits 1-9 set.
//
// Strategy 1, "straight elimination":
// As a cell is set to a known digit, the corresponding bit is eliminated from
// all other cells in the same row, column, and surrounding 3x3 block.
//
// When all known digits have been eliminated from their neigbouring cells,
// hopefully some cells have only one bit left, which can then be converted to
// a "known" digit, generating yet more eliminations.
//
// In this ways some trivial sudokus can be be completely solved.
//
// Strategy 2, "find singletons":
// This strategy counts the number of occurrences of each possible number
// in a row, column, or block, and look for numbers only possible in a
// single cell.  E.g. if, after elimination, three cells in some
// row are left open with the possibilities [2,3], [2,3], and [3,8], then 8
// must be in the thrid cell. I call such numbers "singletons".
//
// The code iteratively searches for singletons and performs elimination until
// no more digits can be found.
//
// Strategy 3: recursive trial-and-error:
// In the finaly strategy, the code recursively selects the cell with the least
// number of possibilities and tries to set the cell to each possible digit.
// As each digit is tried, the strategy recursively calls the solver, which
// invokes elimination, finds singletons and proceeds to the next "open" cell,
// until either a conflict is found or the problem solved.
// Backtracking is done by simply returning from a recursive call, which
// throws away the local solution candidate and lets the level above proceed
// to the next possibility. Exhausting the possibilities also causes
// backtracking.
//
//

// Sudoku size. 4, 9 and 16 are possible, selected by ChooseSize below. 
LOCAL siz=9;

// Forward declaration of the getUnknownsValue method, which
// returns the negative number representing a fully unknown cell.
getUnknownsValue();

// The sudoku grid.
LOCAL displayGrid=MAKEMAT(getUnknownsValue(),siz,siz);

// The undo list
LOCAL undoList = {};

// The row and column of the highlighted cell in the editor, if any. 0 == none.
LOCAL highlightR=0,highlightC=0;

// Forward declarations of solver subroutines.
Solve();
EliminateAll();
recursiveSolve();
getGroups();
getNeighbours();
groupsList();
checkAndEliminate();

// Forward declaration of display subroutines
DisplayCell();
DisplayGrid();
DisplayTime();

// Forward declaration of editor subroutines
ChooseSize();
DoClear();
DoSolve();
DoDraw();
DoHelp();
ChooseDemo();
HighlightOn();
HighlightOff();
HighlightMove();
resetFreeCells();
pushGrid();
popGrid();
DoUndo();
ShowHint();

// Utility routines
BitS();
BitCount();
Union();

// Sudoku editor/solver main entry. Only exported routine.
// Clears the screen, display the menu, then loops waiting for key or mouse
// input.
// Mouse: If in the menu, act on menu items.
//        If in the sudoku, select and highlight a cell
// Keypad: If highlight is on, move the highlight.
// Key: ESC: Exit.
//      digit (0-9, A-F): If digit valid for selected suduko size and highlight
//                        is on: Set highlighted cell to digit.
EXPORT Sudoku()
BEGIN
  LOCAL digitKeys={47,42,43,44,37,38,39,32,33,34,14,15,16,17,18};
  LOCAL joystick={7,8,2,12};
  siz:=9;
  displayGrid:=MAKEMAT(getUnknownsValue(),siz,siz);
  undoList:={};
  highlightR:=0;
  highlightC:=0;

  DoClear();
  DoDraw();
  
  WHILE 1 DO
    LOCAL w=WAIT(−1),p=−1;
    CASE
    IF TYPE(w)≤1 THEN // Key
      CASE
      IF w==4 THEN // Esc
        BREAK; 
      END;
      IF w==3 THEN // Help
        ShowHint();
      END;
      IF w==19 THEN // Del
        IF highlightR AND highlightC THEN
            pushGrid();
            displayGrid(highlightR,highlightC):=getUnknownsValue();
            displayGrid:=resetFreeCells(displayGrid);
            DoDraw();
            HighlightOn(highlightR,highlightC);  
        END;  
 
      END;

      IF p:=POS(digitKeys,w) THEN // 0-9,A-F
        // PRINT("Digit="+(p-1));
        LOCAL digit=p-1; // 0-15
        CASE
          IF siz==4 THEN // Accept 1-4
            IF digit<1 OR digit>siz THEN
              CONTINUE;
            END; 
          END;
          IF siz==9 THEN // Accept 1-9
            IF digit<1 OR digit>siz THEN
              CONTINUE;
            END; 
          END;
          DEFAULT // Accept 0-15
            IF digit<0 OR digit≥siz THEN
              CONTINUE;
            END; 
        END;
        
        IF highlightR AND highlightC THEN
            pushGrid();
            IFERR
              
              displayGrid:=checkAndEliminate(displayGrid,highlightR,highlightC,digit,0);

            THEN
              MSGBOX("Not possible!");
              DoUndo();
            END;  
        END;  
      END;
      
      IF p:=POS(joystick,w) THEN
        CASE
          IF p==1 THEN HighlightMove(0,−1); END;
          IF p==2 THEN HighlightMove(0,1); END;
          IF p==3 THEN HighlightMove(−1,0); END;
          IF p==4 THEN HighlightMove(1,0); END;
          DEFAULT
        END;

      END;
      DEFAULT
        // Ignore other keys
      END; 
    END; 
    IF TYPE(w)==6 THEN // Mouse
      LOCAL ev=w(1);
      IF ev==3 THEN // click
        LOCAL x=B→R(w(2)),y=B→R(w(3));
        //PRINT("(x,y)="+x+","+y);
        IF y≥220 THEN // in menu
          LOCAL m=IP(x/54);
          CASE
            IF m==0 THEN ChooseSize(); END;
            IF m==1 THEN DoClear(); END;
            IF m==2 THEN DoHelp(); END;
            IF m==3 THEN 
               HighlightOff();
               LOCAL d=ChooseDemo();
               siz:=colDim(d);  
               displayGrid:=resetFreeCells(d);
               DoDraw(); 
            END;
            IF m==4 THEN 
               DoUndo();
            END;
            IF m==5 THEN
               pushGrid();
               DoSolve();
            END;

            DEFAULT
              // Ignore
          END;
        ELSE
          // mouse, not in menu
          LOCAL r=IP(y/12),c=IP(x/12);
          IF 1≤r≤siz AND 1≤c≤siz THEN
            // within displayGrid
            HighlightOn(r,c); 
          ELSE
            // outside displayGrid
            HighlightOff();
          END; 
        END;
      END;
    END;
    DEFAULT
    // Unknown WAIT return type
    //PRINT(w);
    END;
  END;
END;

ShowHint()
BEGIN
  IF highlightR AND highlightC THEN
    MSGBOX("Possible digits are: "+STRING(BitS(displayGrid(highlightR,highlightC))));
    DoDraw();
    HighlightOn(highlightR,highlightC);  
  END;
END;

// Display help text
DoHelp()
BEGIN
  // PRINT(displayGrid);
  LOCAL txt={
"Size: Select grid size.",
"Clear: Clear the grid and the undo list.",
"Help: This help.",
"Demo: Select a demo problem.",
"Undo: Undo latest operation.",
"Solve: Solve the problem.",
"",
"Click in the grid to highlight a cell,",
"then enter a digit or use the pad to",
"move the highlight. Del clears a cell.",
"",
"ESC exits the program, HELP shows a hint."};
  RECT();
  LOCAL r=0;
  FOR r FROM 1 TO SIZE(txt) DO
    TEXTOUT_P(txt(r),0,r*14,2);
  END;
  WAIT(−1);

  DoDraw();
END;

// Solve!
DoSolve()
BEGIN
  HighlightOff();
  displayGrid:=Solve(resetFreeCells(displayGrid));
END;

// Draw the grid and display the sudoku
DoDraw()
BEGIN
  RECT();
  DRAWMENU("Size","Clear","Help","Demo","Undo","Solve");
 
  LOCAL r,c,blockSize=√siz;
  LOCAL x0=9,x1=siz*12+9+blockSize+1;
  LOCAL y0=x0,y1=x1;
  LOCAL dy=0;
  FOR r FROM 0 TO siz DO
    LINE_P(x0,y0+dy,x1,y0+dy);
    IF NOT (r MOD blockSize) THEN
      LINE_P(x0,y0+dy+1,x1,y0+dy+1);
      dy+1▶dy;  
    END;
    dy+12▶dy;  
  END;
  LOCAL dx=0;
  FOR c FROM 0 TO siz DO
    LINE_P(x0+dx,y0,x0+dx,y1);   
    IF NOT (c MOD blockSize) THEN
      LINE_P(x0+dx+1,y0,x0+dx+1,y1);
      dx+1▶dx;
    END;
    dx+12▶dx;
  END;
  DisplayGrid(displayGrid);
END;

// Turn on the highlight
HighlightOn(r,c)
BEGIN
  IF highlightR AND highlightC THEN
    HighlightOff();
  END;
  IF 1≤r≤siz AND 1≤c≤siz THEN
    highlightR:=r;
    highlightC:=c;

    LOCAL x0=12+(highlightC-1)*12+IP((highlightC-1)/√siz)-2;
    LOCAL y0=12+(highlightR-1)*12+IP((highlightR-1)/√siz)-2;
    LOCAL x1=x0+12,y1=y0+12;

    
    LINE_P(x0,y0,x1,y0,#FF0000h);  
    LINE_P(x1,y0,x1,y1,#FF0000h);  
    LINE_P(x0,y0,x0,y1,#FF0000h);  
    LINE_P(x0,y1,x1,y1,#FF0000h);  
 
  END;
 
END;

// Turn off the highlight
HighlightOff()
BEGIN
  LOCAL r,c;
  IF highlightR AND highlightC THEN
    LOCAL x0=12+(highlightC-1)*12+IP((highlightC-1)/√siz)-2;
    LOCAL y0=12+(highlightR-1)*12+IP((highlightR-1)/√siz)-2;
    LOCAL x1=x0+12,y1=y0+12;

    
    LINE_P(x0,y0,x1,y0);  
    LINE_P(x1,y0,x1,y1);  
    LINE_P(x0,y0,x0,y1);  
    LINE_P(x0,y1,x1,y1);  
 
  END;
  highlightR:=0;
  highlightC:=0;
END;

// Move the highlight
HighlightMove(dr,dc)
BEGIN
  IF highlightR AND highlightC THEN
    LOCAL hr=highlightR,hc=highlightC;
    HighlightOn(MAX(1,MIN(hr+dr,siz)),MAX(1,MIN(hc+dc,siz)));    
  END;
END;

// Clear the sudoku
DoClear()
BEGIN
  displayGrid:=MAKEMAT(getUnknownsValue(),siz,siz);
  undoList:={};
  HighlightOff();
  DoDraw();
END;

// Undo latest operation
DoUndo()
BEGIN
  IF popGrid() THEN
    LOCAL g=displayGrid;
    siz:=colDim(g);
    DoDraw();
  END;
END;

numOpen(g)
BEGIN
  RETURN ΣLIST(EXECON("IFTE(&1<0,1,0)",g));
END;

// Let the user choose sudoku size
ChooseSize()
BEGIN
  LOCAL m=0,e=0;
  IF CHOOSE(m,"Choose Sudoku size:","Childuko (4×4)","Sudoku (9×9)","Hexadoku (16×16)") THEN
  CASE
    IF m==1 THEN e:=4; END;
    IF m==2 THEN e:=9; END;
    IF m==3 THEN e:=16; END;
    DEFAULT
  END;
  END;
  IF e THEN
    siz:=e;
    DoClear(); 
  END; 
END;

// push the display grid to the undo list
pushGrid()
BEGIN
  undoList:=CONCAT(displayGrid,undoList);
END;

// pop the undo list to the display grid
// Returns: true if there was anything on the undoList
popGrid()
BEGIN
  IF SIZE(undoList) THEN
    LOCAL u=undoList;
    LOCAL g=head(u);
    displayGrid:=g;
    LOCAL tl=tail(u);
    undoList:=tl;
    RETURN 1;
  ELSE
    RETURN 0;
  END;
END;

// Returns the "unknowns value", i.e. the negative bitmask representing
// all possible digits.
// Note: 4x4 -> 1..4 -> -30, 9x9 -> 1..9 -> -1022, 16x16 -> -65535  
getUnknownsValue()
BEGIN
  LOCAL a=2^siz-1;
  IF siz<16 THEN
    a:=2*a;
  END;
  RETURN -a;
END;

// Reset unset values to correct possibilities. Needed after clearing of a know value.
resetFreeCells(grid)
BEGIN
  LOCAL r,c,u=getUnknownsValue();
  FOR r FROM 1 TO siz DO
    FOR c FROM 1 TO siz DO
      IF grid(r,c)<0 THEN
        grid(r,c):=u;
      END;
    END;
  END;
  RETURN grid;
  //RETURN EliminateAll(grid,0,0,0,0);
END;

// Solve the sudoku and display the total running time
Solve(grid)
BEGIN
  LOCAL start=Time;
  DisplayGrid(grid);
  grid:=EliminateAll(grid,0,0,0,1); 
  grid:=recursiveSolve(grid);
  DisplayGrid(grid); 
  LOCAL stop=Time;
  LOCAL elap=stop-start;
  DisplayTime(elap);
  RETURN grid;
END;

// Display the entire sudoku (numbers only, grid is drawn by DoDraw()).
DisplayGrid(grid)
BEGIN
  LOCAL r,c;
  FOR r FROM 1 TO siz DO
    FOR c FROM 1 TO siz DO
      DisplayCell(r,c,grid(r,c)); 
    END;
  END;
END;

// Displays a single cell
DisplayCell(r,c,d)
BEGIN
  LOCAL e=d,s;
  LOCAL x=12+(c-1)*12+IP((c-1)/√siz)+1;
  LOCAL y=12+(r-1)*12+IP((r-1)/√siz);
  IF d<0 THEN
    s:="  ";
    RECT_P(x-2,y-1,x+7,y+8,#FFFFFFh,#FFFFFFh);
  ELSE
    IF e≤9 THEN
      s:=CHAR(48+e);
    ELSE
      s:=CHAR(65+e-10); 
    END;
    TEXTOUT_P(s,x,y,1,0,9,#FFFFFFh);
  END;
END;

// Displays a string to the right of the sudoku
DisplayTime(t)
BEGIN
  TEXTOUT_P(STRING(t),siz*12+√siz+12+2,8);
END;

// Returns a list of all set/known cells in the grid, i.e. all cells
// with positive numbers.
// Returns: {{r1,c1},{r2,c2},...}
allSet(grid)
BEGIN
  LOCAL r=0,c=0,elm={};
  FOR r FROM 1 TO siz DO
    FOR c FROM 1 TO siz DO
      IF grid(r,c)≥0 THEN
        elm:=CONCAT(elm,{{r,c}});
      END;
    END;
  END;
  RETURN elm;
END;

// Set, if possible, the cell at grid(r,c) to d, and eliminates from that cell.
// Throws an error if not possible.
// If indirect is true then proceeds to indirect eliminations.
// Returns new grid
checkAndEliminate(grid,r,c,d,indirect)
BEGIN
  IF grid(r,c)==d THEN
    RETURN grid;
  END;
  IF grid(r,c)>0 THEN
    1/0; 
  END;
  IF BITAND(R→B(-grid(r,c)),BitSL(#1,d)) THEN 
    DisplayCell(r,c,d);
    RETURN EliminateAll(grid,r,c,d,indirect);
  ELSE
    1/0;
  END;
END;

// Returns a list of all "groups" in the grid, i.e. all rows, columns, and
// blocks, as lists of coordinates.
// Returns: {group1, group2,...}
// where a group is: {{r1,c1},{r2,c2},...}
// Note: This is constant for a sudoku of given size, and is cached.
LOCAL aSiz=0,aG={};
allGroups()
BEGIN
  IF aSiz==siz AND SIZE(aG) THEN
    RETURN aG;
  END;
  aSiz:=siz;
  LOCAL lst={};
  LOCAL row,col,g;
  FOR row FROM 1 TO siz DO
    g:={};
    FOR col FROM 1 TO siz DO
      g:=CONCAT(g,{{row,col}}) 
    END;
    lst:=CONCAT(lst,{g})
  END;
  FOR col FROM 1 TO siz DO
    g:={};
    FOR row FROM 1 TO siz DO
      g:=CONCAT(g,{{row,col}}) 
    END;
    lst:=CONCAT(lst,{g})
  END;
  LOCAL bRow,bCol,blockSize=√siz;
  FOR bRow FROM 1 TO blockSize DO
    FOR bCol FROM 1 TO blockSize DO
      g:={};
      LOCAL r0=(bRow-1)*blockSize+1;
      LOCAL r1=r0+blockSize-1;
      LOCAL c0=(bCol-1)*blockSize+1;
      LOCAL c1=c0+blockSize-1;
      FOR row FROM r0 TO r1 DO
        FOR col FROM c0 TO c1 DO
          g:=CONCAT(g,{{row,col}}) 
        END;
      END;
      lst:=CONCAT(lst,{g})
    END;
  END;
  aG:=lst;
  RETURN lst; 
END;

// Returns a list of all singletons in the grid.
// Returns: {{row1,col1,digit1},{row2,col2,digit2},...}
listSingletons(grid,groups)
BEGIN
    LOCAL lst={};
    WHILE SIZE(groups) DO
      LOCAL g:=head(groups);
      groups:=tail(groups);
      LOCAL s=MAKELIST({},X,1,siz+1);
      WHILE SIZE(g) DO
        LOCAL p=head(g);
        LOCAL r=p(1),c=p(2);
        g:=tail(g);
        IF grid(r,c)<0 THEN
          LOCAL b=BitS(grid(r,c));
          WHILE SIZE(b) DO
            LOCAL d=head(b);
            b:=tail(b);
            s(d+1):=CONCAT(s(d+1),{p});
          END;
        END;//IF grid(p)<0
      END;
      LOCAL d;
      FOR d FROM 0 TO siz DO
        LOCAL pl=s(d+1);       
        IF SIZE(pl)==1 THEN
          LOCAL p=head(pl);
          lst:=CONCAT(lst,{CONCAT(p,d)})
        END;
      END;
    END;
  RETURN lst;
END;

// Finds and converts singletons, with elimination. Returns new grid.
// (Not used in this version. EliminateAll now scans for singletons
// created at each elimination.)
findSingletons(grid)
BEGIN
  LOCAL ogrid;
  REPEAT
    ogrid:=grid;
    LOCAL lst=listSingletons(grid,allGroups()); 
    WHILE SIZE(lst) DO
      LOCAL p=head(lst);
      lst:=tail(lst);
//PRINT("Singleton ("+p(1)+","+p(2)+"):"+p(3));
      grid:=checkAndEliminate(grid,p(1),p(2),p(3),1); 
    END;
  UNTIL grid==ogrid;
  RETURN grid; 
END;

// Returns the open cell with the least number of possibilities.
// Returns: A list of the cell coordinates, e.g. {{r,c},...}.
// The first cell is the cell with least possibilities, the rest are
// unordered.
// (Was originally a complete list of all open positions, sorted by number
// of possibilities, but this was unnecessary and simplified to just a single
// cell.) 
openPositions(grid)
BEGIN
  LOCAL r=0,c=0,p={},m=4711;
  FOR r FROM 1 TO siz DO
    FOR c FROM 1 TO siz DO
      IF grid(r,c)<0 THEN
        LOCAL b=BitCount(grid(r,c));
        IF b<m THEN
          m:=b;
          p:=CONCAT({{r,c}},p);
        ELSE
          p:=CONCAT(p,{{r,c}});
        END;
      END; 
    END;
  END;
  RETURN p;
END;

// Recursive solver.
recursiveSolve(grid)
BEGIN
//  grid:=findSingletons(grid);
  LOCAL opn=openPositions(grid);
  IF SIZE(opn) THEN
    LOCAL o=head(opn);
    LOCAL r=o(1),c=o(2);
    LOCAL cnds=BitS(grid(r,c));
    WHILE SIZE(cnds) DO
      //PRINT("Trying ("+r+","+c+")="+STRING(cnds));
      LOCAL cnd=head(cnds);
      cnds:=tail(cnds);
      
      IFERR
        //PRINT("Trying ("+r+","+c+")="+cnd+", was "+grid(r,c));
        LOCAL g2=checkAndEliminate(grid,r,c,cnd,1);
        grid:=recursiveSolve(g2);
      THEN
        // Just try next candidate
        DisplayGrid(grid);
        //PRINT("Backtrack!");
      ELSE
        RETURN grid; 
      END;
 
    END;
    // No solution
    1/0;
  ELSE
    //PRINT("Solution!");
  END;
  RETURN grid;
END;

// Eliminates possibilities from neighbouring cells.
// If r or c ==0, then eliminate starting from all known cells.
// If r and c ≠0, then set grid(r,c) to d and eliminate from that cell only.
// If indirect is true, then proceeds to indirect eliminations.
// Returns new grid.
EliminateAll(grid,r,c,d,indirect)
BEGIN
  LOCAL elm={},groups={};
  IF r==0  OR c==0 THEN
    elm:=allSet(grid);
  ELSE
    elm:={{r,c}};
    grid(r,c):=d;
  END;
  WHILE SIZE(groups) OR SIZE(elm) DO
    WHILE SIZE(elm) DO
        LOCAL h=head(elm);
        r:=h(1);
        c:=h(2);
        elm:=tail(elm);
        d:=grid(r,c);
        groups:=Union(groups,getGroups(r,c));
//        PRINT("groups="+SIZE(groups));
        LOCAL m=BitSL(1,d);
        LOCAL n=BITNOT(m);
        LOCAL lst=getNeighbours(grid,r,c);
        WHILE SIZE(lst) DO
          LOCAL e=head(lst);
          lst:=tail(lst);
          LOCAL x=e(1),y=e(2);
          LOCAL p=grid(x,y);
          IF p<0 AND BITAND(−p,m) THEN
            LOCAL q=−BITAND(−p,n);
            IF q≠0 THEN
              IF indirect AND BitCount(q)==1 THEN
                LOCAL fnd=BitS(q);
                DisplayCell(x,y,fnd(1));
//                PRINT("Found ("+x+","+y+"):"+STRING(fnd));
                grid(x,y):=fnd(1);
                elm:=Union(elm,{{x,y}});
              ELSE
                grid(x,y):=q;
                groups:=Union(groups,getGroups(x,y)); 
              END;
            END;
          ELSE // p≥0
            IF p==d THEN
              //PRINT("Can't take "+d+" from ("+x+","+y+")");
              1/0;
            END; 
          END; 
        END; // WHILE lst
    END; // WHILE elm
    
    IF SIZE(groups) THEN
      LOCAL lst=listSingletons(grid,groupsList(groups)); 
      WHILE SIZE(lst) DO
        LOCAL p=head(lst);
        lst:=tail(lst);
        LOCAL r=p(1),c=p(2),d=p(3);
//PRINT("ESingleton ("+r+","+c+"):"+d);
        grid(r,c):=d;
        DisplayCell(r,c,d);
        elm:=Union(elm,{{r,c}});
      END;
      groups:={};
    END;
  END; // WHILE groups OR elm
  
  RETURN grid;
END;

// Returns a list of all cells "neighbouring" the given cell
// (i.e. in the same row, column, or block)
getNeighbours(grid,r,c)
BEGIN
  LOCAL lst={};
  LOCAL row,col;
  FOR col FROM 1 TO siz DO
    IF col≠c THEN
      lst:=Union(lst,{{r,col}}) 
    END; 
  END;
  FOR row FROM 1 TO siz DO
    IF row≠r THEN
      lst:=Union(lst,{{row,c}}); 
    END; 
  END;
  LOCAL blockSize=√siz;
  LOCAL r0=r-((r-1) MOD blockSize);
  LOCAL r1=r0+blockSize-1;
  LOCAL c0=c-((c-1) MOD blockSize);
  LOCAL c1=c0+blockSize-1;
  FOR row FROM r0 TO r1 DO
    FOR col FROM c0 TO c1 DO
      IF r≠row AND c≠col THEN
        lst:=Union(lst,{{row,col}});
      END;
    END;
  END;
  RETURN lst; 
END;

// Returns a list of all the groups a cell belongs to
// (i.e. the same row, column, and block)
// Returns: {group1, group2,...}
// where a group is represented by an index:
// Rows are numbered 0..siz-1 (i.e. row 2 has index 1),
// Cols are numbered siz..2*siz-1 (i.e. col 2 has index siz+1),
// and blocks are numbered 2*siz..3*siz-1.
// This format is used to simplify the collection of groups
// in a set.
getGroups(r,c)
BEGIN
  LOCAL lst={};
  lst(0):=r-1; 
  lst(0):=siz+c-1;
  LOCAL blockSize=√siz;
  LOCAL r0=(r-((r-1) MOD blockSize)-1)/blockSize;
  LOCAL c0=(c-((c-1) MOD blockSize)-1)/blockSize;
  lst(0):=siz+siz+r0*blockSize+c0;
//  PRINT("getGroups("+r+","+c+")="+STRING(lst));
  RETURN lst; 
END;

// Converts a list of group indices to a list of "real" groups,
// in the same format use returned by allGroups() and expected
// by listSingletons().
groupsList(g)
BEGIN
  LOCAL lst={};
  WHILE SIZE(g) DO
    LOCAL group={};
    LOCAL h:=head(g);
    g:=tail(g);
    LOCAL row,col,blockSize=√siz;
    CASE
    IF h<siz THEN
      LOCAL r=h+1;
      FOR col FROM 1 TO siz DO
        group:=CONCAT(group,{{r,col}});  
      END;
      lst:=CONCAT(lst,{group});
    END;
    IF h<2*siz THEN
      LOCAL c=h-siz+1;
      FOR row FROM 1 TO siz DO
        group:=CONCAT(group,{{row,c}}); 
      END;
      lst:=CONCAT(lst,{group});
    END;
    DEFAULT
      LOCAL b=h-2*siz;
      LOCAL bc=b MOD blockSize;
      LOCAL br=(b-bc)/blockSize; 
      LOCAL r0=br*blockSize+1;
      LOCAL r1=r0+blockSize-1;
      LOCAL c0=bc*blockSize+1;
      LOCAL c1=c0+blockSize-1;
      FOR row FROM r0 TO r1 DO
        FOR col FROM c0 TO c1 DO
          group:=CONCAT(group,{{row,col}});
        END;
      END;
      lst:=CONCAT(lst,{group});
    END;
  END;
//  PRINT("groupList("+g+")="+STRING(lst));
  RETURN lst; 
END;

// A trivial sudoku, from https://projecteuler.net/problem=96
LOCAL easy=[
[-1022,-1022,3,-1022,2,-1022,6,-1022,-1022],
[9,-1022,-1022,3,-1022,5,-1022,-1022,1],
[-1022,-1022,1,8,-1022,6,4,-1022,-1022],
[-1022,-1022,8,1,-1022,2,9,-1022,-1022],
[7,-1022,-1022,-1022,-1022,-1022,-1022,-1022,8],
[-1022,-1022,6,7,-1022,8,2,-1022,-1022],
[-1022,-1022,2,6,-1022,9,5,-1022,-1022],
[8,-1022,-1022,2,-1022,3,-1022,-1022,9],
[-1022,-1022,5,-1022,1,-1022,3,-1022,-1022]];

// A moderately hard sudoku, from https://projecteuler.net/problem=96
// (Number 6 in the problem file)
LOCAL pe06=[
[1,-1022,-1022,9,2,-1022,-1022,-1022,-1022],
[5,2,4,-1022,1,-1022,-1022,-1022,-1022],
[-1022,-1022,-1022,-1022,-1022,-1022,-1022,7,-1022],
[-1022,5,-1022,-1022,-1022,8,1,-1022,2],
[-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022],
[4,-1022,2,7,-1022,-1022,-1022,9,-1022],
[-1022,6,-1022,-1022,-1022,-1022,-1022,-1022,-1022],
[-1022,-1022,-1022,-1022,3,-1022,9,4,5],
[-1022,-1022,-1022,-1022,7,1,-1022,-1022,6]];

// A "hard", sudoku, from http://www.telegraph.co.uk/science/science-news/9359579/Worlds-hardest-sudoku-can-you-crack-it.html
LOCAL hard=[
[8,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022],
[-1022,-1022,3,6,-1022,-1022,-1022,-1022,-1022],
[-1022,7,-1022,-1022,9,-1022,2,-1022,-1022],
[-1022,5,-1022,-1022,-1022,7,-1022,-1022,-1022],
[-1022,-1022,-1022,-1022,4,5,7,-1022,-1022],
[-1022,-1022,-1022,1,-1022,-1022,-1022,3,-1022],
[-1022,-1022,1,-1022,-1022,-1022,-1022,6,8],
[-1022,-1022,8,5,-1022,-1022,-1022,1,-1022],
[-1022,9,-1022,-1022,-1022,-1022,4,-1022,-1022]];

// A 16x16 "hexadoku"
LOCAL hexa=[
[-65535,-65535,-65535, 6,-65535,11,-65535,-65535,-65535,-65535,-65535,10,-65535,15, 1,-65535],
[-65535, 3,-65535,-65535,-65535, 1,-65535, 9,12,-65535,-65535,11,-65535,-65535,-65535,-65535],
[-65535,-65535, 2, 5, 3, 7,-65535,-65535,15,-65535,-65535, 1, 9,11,-65535,-65535],
[-65535, 4,15,-65535,-65535,-65535,12,-65535,-65535,-65535, 0,-65535,-65535,-65535,-65535, 8],
[ 8, 2,-65535,-65535,-65535,-65535,10,-65535,-65535, 5, 4,-65535,-65535,-65535,-65535,-65535],
[10,-65535,-65535,-65535, 7,-65535, 4, 0, 9,-65535, 2,-65535,-65535,-65535,-65535,-65535],
[-65535,14,-65535,-65535,-65535, 8,-65535,-65535,-65535,-65535, 3,15, 4, 5,-65535,-65535],
[ 3, 5,-65535,-65535,-65535,-65535,-65535,13,-65535, 6,-65535,-65535,-65535, 8,15,-65535],
[ 1,-65535,-65535, 9,-65535, 6,-65535,-65535,13,-65535,-65535,-65535, 2,-65535,-65535,-65535],
[-65535, 6,13,-65535,-65535, 9, 3,12,-65535,10,-65535,-65535,-65535,-65535,-65535,-65535],
[15,-65535,-65535,-65535,-65535,-65535,-65535,-65535,11,-65535, 9,-65535,-65535,-65535,-65535, 7],
[-65535, 8,-65535,-65535,-65535,-65535,-65535, 5, 6,12,-65535,-65535,-65535, 0,13,-65535],
[13,-65535,-65535,-65535,-65535, 4, 7,-65535, 2,-65535,-65535, 6,-65535,-65535,-65535,-65535],
[14, 1,-65535,-65535,-65535,-65535, 5,-65535,-65535,-65535,-65535,-65535, 7,10, 0,-65535],
[-65535,-65535, 8,-65535,-65535,14,13,-65535, 1,15,-65535, 9,-65535, 2, 4,11],
[-65535,-65535, 0,12,15,-65535,-65535,-65535, 8,-65535,-65535,-65535,14,-65535,-65535,-65535]];

// A tiny "childoku"
LOCAL tiny=[
[1,2,-30,-30],
[3,4,-30,-30],
[-30,-30,-30,-30],
[-30,-30,-30,-30]];

// Display a menu of the predefined suduko tests above.
// Returns the choosen grid, or 0 if none.
ChooseDemo()
BEGIN
  LOCAL m=0,e=0;
  IF CHOOSE(m,"Choose Sudoku test:","tiny (4×4)","easy (9×9)","pe06 (9×9)","hard (9×9)","hexa (16×16)") THEN
  CASE
    IF m==1 THEN e:=tiny; END;
    IF m==2 THEN e:=easy; END;
    IF m==3 THEN e:=pe06; END;
    IF m==4 THEN e:=hard; END;
    IF m==5 THEN e:=hexa; END;
    DEFAULT
  END;
  END;
  //PRINT("m="+m);
  //PRINT(e);
  RETURN e;
END;

// Returns the bits set in the absolute value of a number, as a list.
// E.g. BitS(10) == {1,3}.
// Note: 10 == 2^1 + 2^3.  
BitS(x)
BEGIN
  IF TYPE(x)==0 THEN
    x:=R→B(ABS(x));
  END;
  LOCAL bits={},y=0;
  WHILE x ≠ #0 DO
    IF BITAND(x,#1) THEN
      bits:=CONCAT(bits,y);
    END;
    x:=BITSR(x);
    y:=y+1;
  END;
  RETURN bits;
END;

// Returns the number of bits in the absolute value of number.
// Algorithm from http://stackoverflow.com/questions/109023/how-to-count-the-number-of-set-bits-in-a-32-bit-integer
BitCount(x)
BEGIN
  IF TYPE(x)==0 THEN
    x:=R→B(ABS(x));
  END;
  x:=x - BITAND(BITSR(x),#55555555h);
  x:=BITAND(x,#33333333h) + BITAND(BITSR(x,2),#33333333h);
  x:=BITSR(BITAND(x+BITSR(x,4),#0F0F0F0Fh)*#01010101h,24);
  RETURN B→R(x);
END;

// Returns the union of two lists, i.e. concatenation without duplicates.
Union(a,b)
BEGIN
  RETURN CONCAT(DIFFERENCE(a,b),INTERSECT(a,b));
END;

/Lennart Börjeson
Stockholm, Sweden
Find all posts by this user
Quote this message in a reply
Post Reply 


Messages In This Thread
Sudoku editor/Solver - lenborje - 12-02-2014, 08:29 PM
RE: Sudoku editor/Solver - Mic - 12-03-2014, 07:10 PM
RE: Sudoku editor/Solver - lenborje - 12-04-2014 07:06 AM
RE: Sudoku editor/Solver - lenborje - 12-04-2014, 01:18 PM
RE: Sudoku editor/Solver - Gerald H - 12-08-2014, 03:14 PM
RE: Sudoku editor/Solver - lenborje - 12-09-2014, 07:58 AM
RE: Sudoku editor/Solver - Han - 12-09-2014, 12:55 AM
RE: Sudoku editor/Solver - lenborje - 12-09-2014, 07:51 AM
RE: Sudoku editor/Solver - Han - 12-09-2014, 01:48 PM
RE: Sudoku editor/Solver - lenborje - 01-31-2015, 02:26 PM
RE: Sudoku editor/Solver - Thomas_Sch - 01-31-2015, 04:14 PM
RE: Sudoku editor/Solver - lenborje - 02-01-2015, 01:26 PM
RE: Sudoku editor/Solver - Thomas_Sch - 02-01-2015, 01:48 PM
RE: Sudoku editor/Solver - lenborje - 02-01-2015, 08:52 PM
RE: Sudoku editor/Solver - Thomas_Sch - 02-02-2015, 08:12 AM
RE: Sudoku editor/Solver - debrouxl - 02-02-2015, 08:33 AM
RE: Sudoku editor/Solver - Guenter Schink - 12-20-2016, 03:37 PM
RE: Sudoku editor/Solver - lenborje - 12-20-2016, 03:40 PM
RE: Sudoku editor/Solver - Guenter Schink - 12-20-2016, 04:11 PM
RE: Sudoku editor/Solver - lenborje - 12-21-2016, 02:57 PM
RE: Sudoku editor/Solver - Guenter Schink - 12-21-2016, 04:25 PM
RE: Sudoku editor/Solver - lenborje - 12-21-2016, 04:42 PM
RE: Sudoku editor/Solver - Guenter Schink - 12-21-2016, 05:02 PM
RE: Sudoku editor/Solver - debrouxl - 12-20-2016, 07:35 PM
RE: Sudoku editor/Solver - lenborje - 12-20-2016, 07:37 PM
RE: Sudoku editor/Solver - lenborje - 12-22-2016, 08:57 AM



User(s) browsing this thread: 9 Guest(s)