Выкладываю свое решение. То, которое было сначала, ничего не менял, только код от комментированного логирования почистил.
MODULE P307Sudoku;
TYPE
Game* = RECORD
fixed-: BOOLEAN;
board-: ARRAY 9, 9 OF RECORD
fixed-: BOOLEAN;
val-: INTEGER; (* 0 = не заполнено *)
END;
possible: RECORD
hor, ver: ARRAY 9 OF SET;
areas: ARRAY 3, 3 OF SET
END
END;
PROCEDURE (VAR g: Game) Init*, NEW;
VAR i, j: INTEGER;
BEGIN
g.fixed := FALSE;
FOR i := 0 TO 8 DO
FOR j := 0 TO 8 DO
g.board[i, j].fixed := FALSE;
g.board[i, j].val := 0;
END
END;
FOR i := 0 TO 8 DO
g.possible.hor[i] := {1..9};
g.possible.ver[i] := {1..9};
END;
FOR i := 0 TO 2 DO
FOR j := 0 TO 2 DO
g.possible.areas[i, j] := {1..9}
END
END
END Init;
PROCEDURE (VAR g: Game) Fix*, NEW;
BEGIN
ASSERT(~g.fixed, 20);
g.fixed := TRUE
END Fix;
PROCEDURE (VAR g: Game) Possible* (col, row: INTEGER): SET, NEW;
VAR arC, arR: INTEGER;
BEGIN
arC := col DIV 3; arR := row DIV 3;
RETURN g.possible.hor[row] * g.possible.ver[col] * g.possible.areas[arC][arR]
END Possible;
PROCEDURE ^ TruncPoss (VAR g: Game; col, row, val: INTEGER);
PROCEDURE ^ ExtendPoss (VAR g: Game; col, row, val: INTEGER);
PROCEDURE (VAR g: Game) Set* (col, row, val: INTEGER), NEW;
BEGIN
ASSERT((1 <= val) & (val <= 9), 20);
ASSERT(g.board[col, row].val = 0, 21);
ASSERT(val IN g.Possible(col, row), 22);
g.board[col, row].val := val;
g.board[col, row].fixed := ~g.fixed;
TruncPoss(g, col, row, val)
END Set;
PROCEDURE (VAR g: Game) Reset* (col, row: INTEGER), NEW;
BEGIN
ASSERT(g.fixed, 20);
ASSERT(g.board[col, row].val # 0, 21);
ASSERT(~g.board[col, row].fixed, 22);
ExtendPoss(g, col, row, g.board[col, row].val);
g.board[col, row].val := 0
END Reset;
PROCEDURE TruncPoss (VAR g: Game; col, row, val: INTEGER);
VAR arC, arR: INTEGER;
BEGIN
arC := col DIV 3; arR := row DIV 3;
EXCL(g.possible.ver[col], val);
EXCL(g.possible.hor[row], val);
EXCL(g.possible.areas[arC, arR], val)
END TruncPoss;
PROCEDURE ExtendPoss (VAR g: Game; col, row, val: INTEGER);
VAR arC, arR: INTEGER;
BEGIN
arC := col DIV 3; arR := row DIV 3;
INCL(g.possible.ver[col], val);
INCL(g.possible.hor[row], val);
INCL(g.possible.areas[arC, arR], val)
END ExtendPoss;
END P307Sudoku.
MODULE P307SudokuAI;
IMPORT Sud := P307Sudoku, Services;
PROCEDURE Solve* (VAR g: Sud.Game; OUT solved: BOOLEAN);
VAR cell, col, row, val: INTEGER;
PROCEDURE SearchForw;
BEGIN
row := cell DIV 9; col := cell MOD 9;
WHILE (cell < 81) & ~(g.board[col, row].val = 0) DO
INC(cell);
row := cell DIV 9; col := cell MOD 9
END
END SearchForw;
PROCEDURE SearchBack;
BEGIN
row := cell DIV 9; col := cell MOD 9;
WHILE (cell > 0) & g.board[col, row].fixed DO
DEC(cell);
row := cell DIV 9; col := cell MOD 9
END
END SearchBack;
PROCEDURE Val (from: INTEGER; poss: SET): INTEGER;
BEGIN
INCL(poss, 10);
WHILE ~(from IN poss) DO
INC(from)
END;
RETURN from
END Val;
BEGIN
cell := 0;
SearchForw;
WHILE (0 <= cell) & (cell < 81) DO
val := Val(g.board[col, row].val+1, g.Possible(col, row));
IF g.board[col, row].val # 0 THEN
g.Reset(col, row)
END;
IF val < 10 THEN
g.Set(col, row, val);
INC(cell);
SearchForw
ELSE
DEC(cell);
SearchBack
END;
END;
solved := cell = 81
END Solve;
END P307SudokuAI.
MODULE P307SudokuCmds;
IMPORT Sud := P307Sudoku, AI := P307SudokuAI, Log := StdLog, In := i21sysIn;
VAR
game: Sud.Game;
PROCEDURE Load*;
VAR i, j, val: INTEGER;
BEGIN
game.Init;
In.Open;
FOR j := 0 TO 8 DO
FOR i := 0 TO 8 DO
In.Int(val);
ASSERT(In.done, 20);
ASSERT((0 <= val) & (val <= 9), 21);
IF val # 0 THEN
game.Set(i, j, val)
END
END
END;
game.Fix
END Load;
PROCEDURE Solve*;
VAR solved: BOOLEAN;
BEGIN
AI.Solve(game, solved)
END Solve;
PROCEDURE Show*;
VAR i, j: INTEGER;
BEGIN
Log.Ln;
FOR j := 0 TO 8 DO
FOR i := 0 TO 8 DO
Log.Int(game.board[i, j].val)
END;
Log.Ln
END;
Log.Ln
END Show;
END P307SudokuCmds.